User/Object Specific Permissions

User/Object Specific Permissions

Old forum URL: forums.lhotka.net/forums/t/11076.aspx


tdilbert posted on Sunday, January 22, 2012

Hey Rocky,

First of all, CSLA is awesome. I really like the changes in the framework since the first time a colleague showed me at my previous job back in .NET 2.0. Definitely something I recommend to coders looking for the "best practices" way to build a robust base to their applications.

The security system in CSLA is great, but I've found myself up against a wall now. Basically I have an application where I need to get a little more granular with user access permissions. Here my situation;

I've got an object that represents a "Car" and I want to control access to what properties of the car someone can see (e.g. what type of engine, what colour, how many seats etc.). This is all being handled using the normal CSLA permissions model, everyone is happy.

Now there are a set of features that, unless you're in a certain role, you cannot see - how much it cost to build the car for example. That is also being handled by CSLA - everyone is happy.

Here's the kicker, how do you handle giving a single person access to only see how much it cost to make Blue Cars? The easy answer is make a "Blue car" permission role, but keep in mind, tomorrow they can have red, purple, light grey - basically "colour" is a variable.

What I'm after here is figuring out how to control permissions on a per user per object level. This project requires permissions to the level where they can vary depending on the attributes of the car (i.e. you can see how much certain parts of the car cost, but not all parts). Something I think will require more than a role-based security model.

I've got some ideas, but I guess I’m not the only person to run into this problem - thought I'd reach out to the community and see if anyone already knows the answer I’m looking for or a better way to achieve it.

Once again thanks man, I’m an advocate of CSLA.

JonnyBee replied on Monday, January 23, 2012

Hi,

Rocky has a nice post about this here: http://www.lhotka.net/weblog/PermissionbasedAuthorizationVsRolebasedAuthorization.aspx

Now - your permissions may be based on some data context and in that sense you need to keep track of the "current" context. Your options are

1. Load all custom authorization into the IPrincipal object as Permissions.

2. Create custom authz rules that can check access permissions to users and objects.
     (Another challenge here is that you can only have 1 authz rule per AuthorizationAction/Object/Property).

3. Add code in CanReadProperty/CanWriteProperty/CanExecuteMethod that will check both IPrincipal.IsInRole and "context" or "object".HasPermission

4. Create an AuthorizationService that loads permission on a per object level when user fetch object for edit.
    We've done this within a bank (to many roles to with millions of customers) and combined with a custom Principal object and 2/3 above.

The key factor is speed!!

IsInRole and CanXYZ is called many times so your authorization check should use cached data and not do database access for each check.

Another big challenge is how to administrate these permissions. It is a PITA  to keep overview on User - roles and User - object permissions.

tdilbert replied on Monday, January 23, 2012

Hey Jonny,

Sounds like we're on the same page when it comes to what I'm after here. I think I've got a clever way to go about this, once I've got something to show I'll upload examples.

tiago replied on Monday, April 09, 2012

Hi Jonny,

I'm dealing with the resource based access control issue. A given user can or cannot access a given object according to some business rules.

1) A user can see a folder if he has Read permission on that folder..

2) In my case it's a folder tree, and the user can create folders if he has Manager permission on the parent folder.

One of the solutions is to build a list of the folders where the user has Read and Manager permissions at application startup or page load and "hang it" on my CustomIdentity class.

There could be a simpler solution if only we could access to the DataPortal_Fetch and DataPortal_Create parameters.

The issue concerns:

AuthorizationActions.GetObject
AuthorizationActions.CreateObject

As you know, on these cases, the Target property of the AuthorizationContext is null (there isn't yet an object) but DataPortal_Fetch and DataPortal_Create parameters have the piece of information that is needed to resolve the issue.

On a GetObject action, the Execute method could check for itself whether the user has Read permission on the folder, as the folder ID is the DataPortal_Fetch parameter.

On a CreateObject action, the Execute method could check for itself whether the user has Manager permission on the parent folder, as the parent folder ID is the DataPortal_Create parameter.

Change request: Is it possbile to expose DataPortal_XYZ parameters as some DataPortalContext property?

<Edit>

What I do now is:

  1. on AddObjectAuthorizationRules() add a authz rule DynamicIsInRole, a sub-class of CSLA's IsInRole that doesn't cache the result (CacheResult = false)
  2. on startup/page load, fill the list of CanReadFolders and CanAdminFolders (CustomIdentity properties)
  3. on factory Get, (remove "dynamic" role CurrentFolderRead) check it the folderID is on the CanReadFolders list and add a "dynamic" role CurrentFolderRead
  4. on factory New, (remove  "dynamic" role CurrentFolderAdmin) check it the parentFolderID is on the CanAdminFolders list and add a "dynamic" role CurrentFolderAdmin

</Edit>

edore replied on Monday, April 09, 2012

Hi!

We faced the same need last week and would thought about the same solution than Tiago proposed.  We'd be glad to be able to access the criteria in our own rule...

Thanks to seriously consider this!

Etienne

JonnyBee replied on Monday, April 09, 2012

Drepends upon whether this should be checked/enforced on the client or serverside.

You can call  the static BusinessRules.HasPermission and supply your own object/citeria in the static factory method or DataPortal_XYZ. That object will be supplied as AuthorizationContext.Target.

Update: This would not be possible unless you change the code in BusinessRules.

A possible solution would be to extend the ApplicationContext with a "UserState" object that could be added manually for Get and Create methods in code.

The DataPortal will additionaly always check the permission and supply null as value parameter.

tiago replied on Thursday, April 12, 2012

JonnyBee

Drepends upon whether this should be checked/enforced on the client or serverside.

You can call  the static BusinessRules.HasPermission and supply your own object/citeria in the static factory method or DataPortal_XYZ. That object will be supplied as AuthorizationContext.Target.

Update: This would not be possible unless you change the code in BusinessRules.

A possible solution would be to extend the ApplicationContext with a "UserState" object that could be added manually for Get and Create methods in code.

The DataPortal will additionaly always check the permission and supply null as value parameter.

Since there is no easy workaround, how about exposing DataPortal_XYZ parameters to authz rules as some DataPortalContext property?

RockfordLhotka replied on Thursday, April 12, 2012

I am not sure how this makes sense in the broader scheme of things.

Authz rules are called in various contextual scenarios, many of which are NOT in the context of a data portal call. In fact, most of them are made outside the context of any data portal call.

So I struggle to see the value of adding some data portal context values to the rule args parameter when 99% of the time those values will be null? Your rule still needs to do something valid in that 99% case...

These authz rules are intended primarily to be pro-active. To be used by presentation layer code to PREVENT the attempt to do something that turns out to be invalid.

What you are describing is a scenario where the attempt must be made (you must already be in the data portal call) to find out that the request is invalid.

This is what exceptions are for. Add your check at the top of the DataPortal_XYZ method and throw an exception to indicate failure.

Copyright (c) Marimer LLC