Reputation: 10291
I'm curious about the best approach to data level security in MVC 3. Allow me to paint a picture.
There exists an Event view. This Event lists the name of the event and a list of what players are playing in this event.
Depending on the current user's relationship with that event, each user would get a substantially different view served.
For example, if the user is an Organiser, then the user can view and manage all other User's availability for that event.
If the user is merely partaking in that event, then obviously that user can only manipulate his own availability.
There are more combinations than this.
I know about custom action filters, but that seems to be overkill.
Instead I've gone for an approach where on the Index of the Event, there is a switch statement that will redirect to the appropriate View, e.g. OrganiserEventView or PlayerEventView.
That's the easy bit. I think.
Where it gets messy is that I've used a shared Editor for the enumeration of PlayerModels (part of the Model of the main view) to list the Players. This shared editor itself would also have to respect data-level security.
Am I on the right track, or is there a better way?
Upvotes: 2
Views: 822
Reputation: 813
Duncan,
This is something I encounter in every application. Here's some context: I work in one department in a large university so we have to deal with multiple data sources. Most of our apps have security that integrates university "central IT" security services such as centralized authentication and active directory, as well as "homegrown" roles and permissions.
Our apps need to present different views and actions for different users based on data in both "homegrown" applications (used internally in our dept) plus enterprise data.
Our approach: After looking around a fair bit and not finding a good solution for this problem anywhere, I ended up writing a security framework for our use. I might as well go ahead and describe what it does here. Here's what's involved:
At this point, here's an example flow in Asp.Net MVC:
-- [Inside Permission.IsAllowed] --
To make things easier, I took it one step further and created a AbstractContextProtectedAttribute, which expects a SecurityContextFactory delegate that can create a SecurityContext (given a HTTPRequest, for example) and a Permission to check with the specified SecurityContext. Subclasses of this class can then be used to decorate controller actions.
Phew! So all that let's me now setup a table of Users, Roles, Permissions and map them to each other, define all the Permissions in the database. I wrote a pluggable SecurityPersistenceService that makes the security framework agnostic of the persistence strategy used - we unfortunately have everything from DataReaders, DataAdapters to Linq2Sql and EF. But at least we can write code like this:
[Protected("CanAccessX")] // Checks using default context
public class SomeController
{
[Protected("CanSeeY")] // Checks using default context
public PartialViewResult GetY(<parameters>)
{
var canSeeY_Variation1 = Permission.Get("CanSeeY_Variation1") ;
var y_Variation1_Context = new Y_Variation1_Context { <build your context here> } ;
if (canSeeY_Variation1.IsAllowed(y_variation1_Context))
{
<return variation 1 view>
}
// Y_Variation_2...etc
}
}
And to make this work, at startup I register the appropriate validators:
public class MyNinjectModule
{
public override void Load()
{
// Wire up a persistence service for the security framework
// to use.
SecurityService.SecurityPersistenceService = new MySecurityPersistenceServiceImplementation() ;
// This is what allows the SecurityService to figure out what Validator to use
// in a specified Context to get the User's Roles.
SecurityService.RegisterValidator<Y_Variation1_Context>(new Y_Variation1_ContextValidator(...)) ;
}
}
I am now working on an addition to this framework that let's me perform these checks over enumerables of data, thus infusing all my domain objects with security infrastructure. The only clean way I know of doing this is using AOP. I used to work in Java and used AspectJ. Now I am considering PostSharp.
Hope this provides perspective for thinking about your problem.
Upvotes: 3
Reputation: 30152
The way I see your question, role based security would be the way to go.
If they are the organizer then they are an organizer role. However you need to abstract this a bit. Since an organizer role would then in theory be an organizer to all other events, you need a method that makes this determination and populates the roles upon request so user A is an organizer of event A, but not organizer of event B. This ideally needs to occur before the controller code is accessed so your choices are global.asax.cs or an auth filter.
Upvotes: 1
Reputation: 4343
The answer will change depending on the size, complexity, and projected growth of your application. While having security in the controller and then having different views works there are trade-offs. For example, code/mark-up replication could be a draw back. For complex applications, portlets might help, but that is heavy stuff. A view can change based on security and access roles, you would pass the parameter/data from the controller just like anything else, if it is not already available via the session. I would create a utility method for the views to use so that the logic/rules do not end up in the view.
Upvotes: 1
Reputation: 57877
Using Roles and custom Role Attributes seem like a good fit (though I can say that without code or a better understanding of your codebase, no one can give you the 'best' way) for your problem.
Upvotes: 0