CSLA .NET 3.5 enhancement - object level authorization

CSLA .NET 3.5 enhancement - object level authorization

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


RockfordLhotka posted on Friday, January 18, 2008

The object-level authorization concept has now been formalized into Csla.Security. So instead of implementing (by convention) those four static methods on each type (CanGetObject(), etc), it is all managed now through CSLA itself.

This is an important change, as it allows the data portal and UI code to check authorization in a standard, reusable manner. The result is less code in each object (no need to check authorization in factory methods or a Save() override) and the potential to use authorization in UI frameworks.

There are three parts to this change.

The first part is in your business object implementation itself. Instead of implementing (by convention) the CanAddObject(), CanGetObject(), CanEditObject() and CanDeleteObject() methods in your object, you now implement a single static/Shared method like this:

    protected static void AddObjectAuthorizationRules()
    {
      // add object-level authorization rules here
      AuthorizationRules.AllowCreate(typeof(Project), "ProjectManager");
      AuthorizationRules.AllowEdit(typeof(Project), "ProjectManager");
      AuthorizationRules.AllowDelete(typeof(Project), "ProjectManager");
      AuthorizationRules.AllowDelete(typeof(Project), "Administrator");
    }

This method is called once per AppDomain and associates roles with each operation. You can allow or deny specific roles, just like you can with properties.

The second part of the change is also in the business object implementation. Typically you'd have calls in your factory methods and a Save() override to check authorization. That is no longer necessary because the data portal (client-side) now performs those checks automatically. The Create() method calls CanCreateObject(), the Fetch() method calls CanGetObject(), the Delete() method calls CanDeleteObject(). The Update() method is more complex, and looks at the state of your object to decide whether to call CanCreateObject(), CanEditObject() or CanDeleteObject().

BusinessBase.IsSavable now also checks authorization, so an object is savable only if it is dirty, valid and the user is authorized to do the save. This will make most Windows and WPF apps automatically turn Save buttons on/off based on authorization.

The CslaDataProvider now also uses authorization to turn on/off the AddNew button in a WPF form based on whether the user is authorized to edit the collection (CanEditObject()).

The third part of the change is that you can write UI code (or business object code) to explicitly check the authorization. This is now done by calling static methods on AuthorizationRules, like this:

bool canCreate = Csla.Security.AuthorizationRules.CanCreateObject(typeof(Project));
bool canGet = Csla.Security.AuthorizationRules.CanGetObject(typeof(Project));
bool canEdit = Csla.Security.AuthorizationRules.CanEditObject(typeof(Project));
bool canDelete = Csla.Security.AuthorizationRules.CanDeleteObject(typeof(Project));

 

ajj3085 replied on Friday, January 18, 2008

Sounds very cool.  Can't wait until I finish the part of the project I'm on now, then I can start moving to 3.5!

Jimbo replied on Friday, January 18, 2008

This does not change the fact that you still need to remember to include the static method to add Authorisation Rules. It is still using the same  "convention".   Why can't this be an implementation contract - even if it the method does nothing ?

RockfordLhotka replied on Friday, January 18, 2008

If you know of a way to force the implementation of a static method I’ll make it a contract.

 

I’ve been beating my brains out on this, but no obvious solution presents itself. A static constructor would be an answer, but there’s no simple/reliable way to force a cctor to execute (short of instantiating the type). This is the best compromise I’ve been able to figure out.

 

But if anyone has a better answer I’m all ears!

 

Rocky

 

From: Jimbo [mailto:cslanet@lhotka.net]
Sent: Friday, January 18, 2008 9:09 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] CSLA .NET 3.5 enhancement - object level authorization

 

This does not change the fact that you still need to remember to include the static method to add Authorisation Rules. It is still using the same  "convention".   Why can't this be an implementation contract - even if it the method does nothing ?


trives replied on Thursday, January 24, 2008

Hi Rocky,

Would you consider adding FlattenHierarchy to the binding flags in ObjectAuthorizationRules.GetRoles(Type objectType)?  This would allow the GetMethod call to find a non-private, static method AddObjectAuthorizationRules in a base class of objectType.  That static method would then be able to delegate the task of adding authorization rules.

For example:

public abstract class NewBusinessBase<T> : Csla.BusinessBase<T>
    where T : NewBusinessBase<T>
{
   protected static void AddObjectAuthorizationRules()
   {
       SomeUtility.AddObjectAuthorizationRules(
typeof(T));
   }
}

I image that flattening the hierarchy would be slower, but this hit would occur only once per type.

Tim

 

RockfordLhotka replied on Thursday, January 24, 2008

Ahh, that’s a good idea!

 

Rocky

 

 

From: trives [mailto:cslanet@lhotka.net]
Sent: Thursday, January 24, 2008 2:04 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: CSLA .NET 3.5 enhancement - object level authorization

 

Hi Rocky,

Would you consider adding FlattenHierarchy to the binding flags in ObjectAuthorizationRules.GetRoles(Type objectType)?  This would allow the GetMethod call to find a non-private, static method AddObjectAuthorizationRules in a base class of objectType.  That static method would then be able to delegate the task of adding authorization rules.

For example:

public abstract class NewBusinessBase<T> : Csla.BusinessBase<T>
    where T : NewBusinessBase<T>
{
   protected static void AddObjectAuthorizationRules()
   {
       SomeUtility.AddObjectAuthorizationRules(typeof(T));
   }
}

I image that flattening the hierarchy would be slower, but this hit would occur only once per type.

Tim

 



skagen00 replied on Friday, January 25, 2008

I'd be very interested in the change in binding flags to include FlattenHierarchy too. In fact, it could come in handy with something we're working on right now. (We're refactoring for 3.5 already).

Thanks!

Chris

 

RockfordLhotka replied on Friday, January 25, 2008

I’ve put that change into svn, so it will be in the next release.

 

Rocky

 

 

From: skagen00 [mailto:cslanet@lhotka.net]
Sent: Friday, January 25, 2008 7:59 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: CSLA .NET 3.5 enhancement - object level authorization

 

I'd be very interested in the change in binding flags to include FlattenHierarchy too. In fact, it could come in handy with something we're working on right now. (We're refactoring for 3.5 already).

Thanks!

Chris

 



stefan replied on Thursday, January 24, 2008

I think that aiming for an analogy between property/method-authorization and type-authorization is good.

One big advantage of the current implementation of property/method-authorization is the support for custom authorization, namely achievable by the fact that the CanReadProperty/CanWriteProperty/CanExecuteMethod functions are overridable.
This is a really big plus.

Type-authorization should work the same way.
The automatic type-authorization checks performed in the data portal should delegate to a custom authorization, if implemented.
There should be overridable functions like CanGetObject etc. in each object instance of BusinessBase/BusinessListBase, as these can't be static/shared. But they would be one liners, just calling the static/shared datastructure:
"return Csla.Security.AuthorizationRules.CanGetObject(GetType(T))"

The data portal then would just use 'CanXYZObject' function calls.

Then the analogy would be perfect...


Stefan

RockfordLhotka replied on Thursday, January 24, 2008

That would be nice. Unfortunately you can’t make static methods virtual, so overriding isn’t really an option.

 

However, centralization is an option, because the static methods are public, anyone can call them, so you could centralize your authorization settings.

 

Or you could create an instance of an object to provide the authorization data – and that instance could be a subclass or something. I haven’t thought that through.

 

Rocky

 

 

From: stefan [mailto:cslanet@lhotka.net]
Sent: Thursday, January 24, 2008 10:49 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] CSLA .NET 3.5 enhancement - object level authorization

 

I think that aiming for an analogy between property/method-authorization and type-authorization is good.

One big advantage of the current implementation of property/method-authorization is the support for custom authorization, namely achievable by the fact that the CanReadProperty/CanWriteProperty/CanExecuteMethod functions are overridable.
This is a really big plus.

Type-authorization should work the same way.
The automatic type-authorization checks performed in the data portal should delegate to a custom authorization, if implemented.
There should be overridable functions like CanGetObject etc. in each object instance ob BusinessBase/BusinessListBase, as they can't be static. But they would be one liners, just calling the static/shared datastructure:
"return Csla.Security.AuthorizationRules.CanGetObject(GetType(T))"

The data portal then would just use 'CanXYZObject' function calls.

Then the analogy would be perfect...


S tefan


stefan replied on Friday, January 25, 2008

Your are right.
And I forgot that the reason why the CanXYZObject functions had to be shared/static was
- to be able to adapt the UI to the users authorization beforehand
- to check the users authorization from inside any shared/static factory methods
  (NewObject,GetObject,...), before invoking the dataportal.
Both cases working without any instances of the object type in question in memory.

Now in 3.5, the (now automatic) authorization checks happen when the dataportal has been invoked.
This implies that there already is an instance of an object in memory, so the
need of having a shared/static mehod is gone in this case.

So there still remains the first case...and I think that there is no way around a shared/static method here.

Stefan

rlarno replied on Thursday, January 24, 2008

Hi Rocky,

I joined the Live Meeting today, great show, got some good pointers on the new features for 3.5. It really looks very promising. Too bad I won't be able to jump onto the 3.5 release for a long while. Sad [:(]

I saw that you added the SetProperty<T>, etc. good stuff. Now is there a reason these object level authorization methods are not using generics? as in:

Csla.Security.AuthorizationRules.AllowCreate<T>(string roleName)

So one could use:

AuthorizationRules.AllowCreate<Project>("ProjectManager");

Since this is a one-off, I would imagine that the overhead of using a generic (even if there is some) is not critical.

Rudi

RockfordLhotka replied on Thursday, January 24, 2008

The reason for not using generics is because that would prevent the use of dynamic loading from metadata. Generics are a compile-time concept, but sometimes you don’t know a type until runtime.

 

If you put your auth rules in a metastore (xml file, database) and then load them at runtime, you don’t necessarily know the type – especially if you do your loading in a centralized service.

 

Rocky

 

 

From: rlarno [mailto:cslanet@lhotka.net]
Sent: Thursday, January 24, 2008 2:43 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] CSLA .NET 3.5 enhancement - object level authorization

 

Hi Rocky,

I joined the Live Meeting today, great show, got some good pointers on the new features for 3.5. It really looks very promising. Too bad I won't be able to jump onto the 3.5 release for a long while. Sad <img src=">

I saw that you added the SetProperty<T>, etc. good stuff. Now is there a reason these object level authorization methods are not using generics? as in:

Csla.Security.AuthorizationRules.AllowCreate<T>(string roleName)

So one could use:

AuthorizationRules.AllowCreate<Project>("ProjectManager");

Since this is a one-off, I would imagine that the overhead of using a generic (even if there is some) is not critical.

Rudi



triplea replied on Friday, January 25, 2008

I really like the change but have a concern. I have implemented a permission system like the one described here: http://forums.lhotka.net/forums/thread/17224.aspx. It works really well for me and I would like to keep it. Basically it allows me to place code in the static CanXYZObject() methods such as:

return PTPrincipal.HasPermission("Create Project")

Am I right by thinking I will not be able to use this functionality? Of course I guess I could always stick on the old way but I would hate to miss out on any new cool CSLA features...

Also, by default if you don't have a AddObjectAuthorizationRules() implementation in your classwould the object happily save (add/update/delete) or will an exception be thrown?

 

RockfordLhotka replied on Friday, January 25, 2008

The object-level auth works like the property-level – which is to say that if you don’t specifically allow/deny any roles then all roles are allowed. The default is permissive.

 

Regarding the HasPermission() thing – I finally implemented something Joe asked for a long time ago. You can now provide a replacement behavior for IsInRole() deep within CSLA. This is a provider model concept, where you write an IsInRole provider that (if I remember right) looks like this:

 

void IsInRole(IPrincipal principal, string role)

 

The default provider in CSLA simply returns principal.IsInRole(role), but you could write a provider like

 

public static void IsInRole(IPrincipal principal, string role)

{

  PTPrincipal.HasPermission(role);

}

 

Or

 

public static void IsInRole(IPrincipal principal, string role)

{

  var p = principal as MyCustomPrincipalType;

  if (p != null)

    return p.HasPermission(role);

  else

    return false;

}

 

Or whatever you need. Then you just add an entry in the app.config file to tell CSLA to load this custom provider and away you go.

 

Rocky

 

 

From: triplea [mailto:cslanet@lhotka.net]
Sent: Friday, January 25, 2008 7:46 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] CSLA .NET 3.5 enhancement - object level authorization

 

I really like the change but have a concern. I have implemented a permission system like the one described here: http://forums.lhotka.net/forums/thread/17224.aspx. It works really well for me and I would like to keep it. Basically it allows me to place code in the static CanXYZObject() methods such as:

return PTPrincipal.HasPermission("Create Project")

Am I right by thinking I will not be able to use this functionality? Of course I guess I could always stick on the old way but I would hate to miss out on any new cool CSLA features...

Also, by default if you don't have a AddObjectAuthorizationRules() implementation in your classwould the object happily save (add/update/delete) or will an exception be thrown?

 



triplea replied on Friday, January 25, 2008

Thanks Rocky but I don't think I quite follow, maybe it was my explanation but I will try again. What I have is an IList<Permission> within MyPrincipal which loads from the database all the permissions the user has. So lets assume 2 roles:

Viewer: "Get Project"
Administrator: "Get Project", "Create Project"

In my Project.CanAddObject() static method I would have the following:

public static bool CanAddObject() { HasPermission("Create Project"); }

Its not so much the HasPermission() method that concerns me (its implemented in my custom base class currently but I can move it as per your comments) but rather how it would fit in the AddObjectAuthorizationRules() implementation. The only way I can think of would be something like this:

    protected static void AddObjectAuthorizationRules()
    {
      // add object-level authorization rules here
      AuthorizationRules.AllowCreate(HasPermission("Create Project"));
    }

That would work fine but I'm not sure this overload will be provided.

RockfordLhotka replied on Friday, January 25, 2008

Ahh.

 

No, in the AddObjectAuthorizationRules() you’d treat your “permissions” like “roles”.

 

    protected static void AddObjectAuthorizationRules()
    {
      // add object-level authorization rules here
      AuthorizationRules.AllowCreate("Create Project");
    }

Then you’d use the technique from my previous post to make sure all “IsInRole” checks actually do a “HasPermission” check instead.

 

Rocky

triplea replied on Friday, January 25, 2008

Hmm it looks a bit odd but if it works then I am happy! Maybe make a note to cover this in the book (if there will be any free space) Smile [:)]

rsbaker0 replied on Friday, February 15, 2008

RockfordLhotka:

The object-level auth works like the property-level – which is to say that if you don’t specifically allow/deny any roles then all roles are allowed. The default is permissive.

 The default provider in CSLA simply returns principal.IsInRole(role), but you could write a provider like

 public static void IsInRole(IPrincipal principal, string role)

{

  PTPrincipal.HasPermission(role);

}

 

Or

 

public static void IsInRole(IPrincipal principal, string role)

{

  var p = principal as MyCustomPrincipalType;

  if (p != null)

    return p.HasPermission(role);

  else

    return false;

}

 

Or whatever you need. Then you just add an entry in the app.config file to tell CSLA to load this custom provider and away you go.

I think I'm missing something.

How can I use this mechanism to have authorization rules or permissions that vary from one object to another of the same class?

We mainly have "write" rules and only a few "read" ones, so what we have been doing for the time being is to implement object-specific rules as business rules rather than authorization rules. If a user modifies inadvertently modifies something they aren't suppose to, we break the object so it can't be saved.

No, it's not perfect but the business rule implementation seems almost infinitely flexible. It would be nice to see the equivalent on the authorization side.

 

 

RockfordLhotka replied on Saturday, February 16, 2008

If you want rules to vary from object instance to instance this is not the technique for you. In that case you would override CanReadProperty() and CanWriteProperty() in your specific business class.

 

The purpose of the IsInRole provider is to allow you to use something other than the standard .NET principal object, or methods on your principal object other than simply IsInRole. For example, at one Magenic client they needed to not only check roles, but also check the user’s department code (an extra property on the Identity) to see if the user was in the correct department as well as the correct role.

 

Rocky

 

 

From: rsbaker0 [mailto:cslanet@lhotka.net]
Sent: Friday, February 15, 2008 7:40 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: CSLA .NET 3.5 enhancement - object level authorization

 

RockfordLhotka:

The object-level auth works like the property-level – which is to say that if you don’t specifically allow/deny any roles then all roles are allowed. The default is permissive.

 The default provider in CSLA simply returns principal.IsInRole(role), but you could write a provider like

 public static void IsInRole(IPrincipal principal, string role)

{

  PTPrincipal.HasPermission(role);

}

 

Or

 

public static void IsInRole(IPrincipal principal, string role)

{

  var p = principal as MyCustomPrincipalType;

  if (p != null)

    return p.HasPermission(role);

  else

    return false;

}

 

Or whatever you need. Then you just add an entry in the app.config file to tell CSLA to load this custom provider and away you go.

I think I'm missing something.

How can I use this mechanism to have authorization rules or permissions that vary from one object to another of the same class?

We mainly have "write" rules and only a few "read" ones, so what we have been doing for the time being is to implement object-specific rules as business rules rather than authorization rules. If a user modifies inadvertently modifies something they aren't suppose to, we break the object so it can't be saved.

No, it's not perfect but the business rule implementation seems almost infinitely flexible. It would be nice to see the equivalent on the authorization side.

 

 



JoeFallon1 replied on Saturday, February 16, 2008

Rocky,

Can you review the 2 posts by Trives starting with Feb 11?

The idea of IsSelfDirty and IsSelfValid has been useful to me over the years that I have been managing contained BOs.

He also asks about implementing an Interface.

Joe

 

RockfordLhotka replied on Saturday, February 16, 2008

I’ll get there Joe :)

 

I just spent two weeks full-time at a client, and so had little time to follow the forum at all, much less do any CSLA work.

 

I saw the self-dirty/valid stuff and that’s also a good idea, which I will add into the framework.

 

I’m not sure I saw the interface post – I’ll have to find that one. Is this the one about a provider pattern for authorization? That probably won’t happen in 3.5 – too big a change, but maybe in 3.5.1 or something. Or is it something else? I’ll certainly add IsSelfValid/Dirty to the existing interfaces if that’s the concern.

 

Now, however, I’m going to spend the rest of the day with my family – being gone for two full weeks is not much fun. I can’t say I recommend it ;)  I don’t think my dog has left my side since I got home. My kids would probably be the same, except the lure of the xbox is too great for them to resist :)

 

Rocky

rsbaker0 replied on Sunday, February 17, 2008

RockfordLhotka:

If you want rules to vary from object instance to instance this is not the technique for you. In that case you would override CanReadProperty() and CanWriteProperty() in your specific business class...

I wasn't so much thinking of changing the semantics of IsInRole() as I was providing a built-in mechanism to inspect the object contents when executing the authorization rules (much like the business rules can), versus looking only at the current user or principal to see if they have a particular permission or are in role without having the object available.

 

RockfordLhotka replied on Sunday, February 17, 2008

Yes, I understand. It is important to realize that there are different questions involved here, each one filling a specific role.

At the top level is can the user read/write/execute something on a given object. That's where you can override CanReadProperty/CanWriteProperty/CanExecuteMethod - to answer that particular question. This ability isn't new - these methods have been virtual since 2.0.

A lower level question is whether the user is in a specific "role" (or has a "permission" or whatever). This question is often required to answer the first question, but not always. The change in 3.5 is to allow you to "override" how this question is answered. This meets a very narrow need, where sometimes the question shouldn't be answered merely in terms of roles, but also in terms of permissions or other user profile data.

Combined, these two extensibility points seem like they should answer most people's need for flexibility, especially since that top level one can be used to entirely replace the auth infrastructure in CSLA if that's necessary.

maurera replied on Wednesday, February 20, 2008

We, too, have an issue with Role Based Application Checks.  The situation we find ourselves in is that we require more "contextual" data to check roles.  I had posted some time ago regarding this, and thought that the IsInRole Delegate solution might meet our needs.  I am now revisiting the issue, and see that it does not (unless I am missing something).

The situation is such that we don't require extra information on the principle to "check permissions" (like your example of the client you mentioned, which requires knowledge of what department the user was in), we require extra information about the state of the object that contains members we require a role check for...

For instance, we have an ItemAdministrator role.  A user with the ItemAdministrator role has a set of subteams associated with that role (specific to that user) which determines what item's they can administer (our items have a subteam property).  For example, user A may be an ItemAdministrator for grocery and meat, while user B may be an ItemAdministrator for Meat and Seafood.  In addition, User A may also have the "Buyer" role for subteam frozen and bread.

This means that when the item is instanced we need to not just ask the question "Is my user in role ItemAdministrator" when modifiying the item's description, we need to ask "Is my user in role ItemAdministrator for the subteam of the current instance of the item they are attempting to administor?"...does this make sense?

If I understand the problem we would have to bypass the authorization structure of CSLA to accomplish our goal...not somethign I want to do as we would like to leverage your existing code..  I suppose we could write our own, but we just don't have the time in the schedule to do this with the initial release. 

I am wondering if we are just missing something fundimental with regards to Role Based Application Checks...

 

RockfordLhotka replied on Wednesday, February 20, 2008

Let's break this down a bit. The Authorization subsystem in CSLA does a series of things.

  1. It stores a list of allow/deny roles for each property
  2. It checks the current principal against the list of allow/deny roles
  3. It caches those results for performance
  4. It uses a swappable delegate so you can change how IsInRole is answered
  5. It allows you to override the CanXyzProperty() methods to override at the top level

If you override CanXyzProperty() you can

  1. Do pre- and post-processing but still use the existing base implementation
  2. Replace the base implementation with your own

If you replace the base implementation you can replicate all the pre-existing functions (leveraging the existing implementations) for everything except caching the results - you'd have to do your own results cache.

But it isn't clear to me how the existing list of allow/deny roles would be useful to you given your requirements? I think you are describing an auth system that is more sophisticated than a normal role-based or permission-based model, so it is no surprise that a role/permission-based model isn't meeting your needs.

Which is why you can override CanXyzProperty() so you can implement non-standard auth systems.

And so my question really, is what part of the existing implementation would you want to reuse, given that it seems like a poor fit overall?

Bob Matthew replied on Wednesday, February 20, 2008

Rocky,

 

I’m experiencing some interesting behavior with the PropertyInfo objects’ Index property.  Specifically, inside of the data portal, which has the exact same assemblies as outside of the data portal, the Index for each PropertyInfo of a business object is different.  This has the ability to call throw an InvalidCastException (because a string property can’t be an int property) or worse yet, not exception at all.

 

I should probably mention that this is most likely due to the fact that I have a custom base class (which for lack of a better name is called BusinessBaseX) that has a single PropertyInfo object (which is common to my objects) being registered.

 

I read through the link you posted previously from the MSDN found here: http://msdn2.microsoft.com/en-us/library/aa645758(VS.71).aspx

 

If you look closely at the beginning of the article, it says that when the static fields are initialized they “are executed in the textual order in which they appear in the class declaration”.  In other words, the only way that they could fire in a different order is by having different versions of an assembly on either side of the portal.

 

This being the case, I wonder if I might re-suggest a re-consideration of the methodology that I was using a few weeks ago before 3.5 Beta 1.  This methodology was the following for the BusinessBase class.

 

// dictionary which maps the property to the index of an array

private static Dictionary<IPropertyInfo, int> _properties = new Dictionary< IPropertyInfo, int>();

 

// instance values of each property

private IFieldData[] _values;

 

I will keep you posted on more of the specifics of the problem as I debug, as well as potential workarounds.

Bob Matthew replied on Wednesday, February 20, 2008

I meant to post this under the 3.5 Beta 1 thread, so I'll repost there.

maurera replied on Thursday, February 21, 2008

Rocky, I intend to get back to you on your question, unfortunatly we are very slammed right now.  I should have time to anwer your question sometime this weekend I think.

maurera replied on Tuesday, February 26, 2008

I still have not been able to find some time to review the Authorization mechanism to determine exactly what we want to leverage from your existing code.  I still intend to do a more complete analysis of this at some point, but who knows when I will have the time.

If I remember correctly though, we thought we would be able to leverage your Role Caching.  The concept being that our Roles can still be represented as a string…just not a static one.  I think the concept was that, instead of “baking in” the Role via AllowXyz, we would want to do something like

Dim AuthRole as Role = Roles.NewRole(“ItemAdminstrator”)

AuthRole.SetContext(me.RetailSubTeam)

AllowWrite(“ItemDescription”, AuthRole)

Where the RetailSubTeam value is passed into SetContext by reference.  The ultimate call to IsInRole via the CanXyz methods would pass in AuthRole.ToString, which would generate the String “ItemAdministrator\1000” (where 1000 is the ID of the item’s RetailSubTeam).  This string would be cached as per normal CSLA execution. 

The biggest problem I see here is that this list could grow considerably.  For instance, if we have ten properties, each with five roles that could potentially write to the property, and each role could be for any of the Seventy Four sub teams you can begin to see how large this list could become.  This doesn’t factor in some of our roles will require both SubTeam and StoreNo for their “context” data.

This is just off the top of my head, and obviously wouldn’t work without us rewriting much of CSLA’s authorization mechanism From the CanXyz method down….but I think that was the general concept we were thinking. 

I will try and do a more detailed analysis sometime this week.  As of now, we are planning on putting off any complex IsInRole checking until later this year.  For the initial release of the application we are going to use “standard” roles that don’t require contextual data.  We think we can get away with this since the initial release will be a small subset of users.  When the time comes we will either write our own, or revisit the capabilities of CSLA’s authorization mechanism at that time.

Please don’t delay the writing of your book or finalization of CSLA 3.5 on our account; we would much prefer your book to get out sooner rather than later.

 

 

RockfordLhotka replied on Tuesday, February 26, 2008

If you overload CanReadProperty/CanWriteProperty you can still use my role caching. It is specifically designed so you can still use the cached roles, but change the rules around how they are used.

 

Rocky

 

 

From: maurera [mailto:cslanet@lhotka.net]
Sent: Tuesday, February 26, 2008 10:18 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: CSLA .NET 3.5 enhancement - object level authorization

 

I still have not been able to find some time to review the Authorization mechanism to determine exactly what we want to leverage from your existing code.  I still intend to do a more complete analysis of this at some point, but who knows when I will have the time.

If I remember correctly though, we thought we would be able to leverage your Role Caching.  The concept being that our Roles can still be represented as a string…just not a static one.  I think the concept was that, instead of “baking in” the Role via AllowXyz, we would want to do something like

Dim AuthRole as Role = Roles.NewRole(“ItemAdminstrator”)

AuthRole.SetContext(me.RetailSubTeam)

AllowWrite(“ItemDescription”, AuthRole)

Where the RetailSubTeam value is passed into SetContext by reference.  The ultimate call to IsInRole via the CanXyz methods would pass in AuthRole.ToString, which would generate the String “ItemAdministrator\1000” (where 1000 is the ID of the item’s RetailSubTeam).  This string would be cached as per normal CSLA execution. 

The biggest problem I see here is that this list could grow considerably.  For instance, if we have ten properties, each with five roles that could potentially write to the property, and each role could be for any of the Seventy Four sub teams you can begin to see how large this list could become.  This doesn’t factor in some of our roles will require both SubTeam and StoreNo for their “context” data.

This is just off the top of my head, and obviously wouldn’t work without us rewriting much of CSLA’s authorization mechanism From the CanXyz method down….but I think that was the general concept we were thinking. 

I will try and do a more detailed analysis sometime this week.  As of now, we are planning on putting off any complex IsInRole checking until later this year.  For the initial release of the application we are going to use “standard” roles that don’t require contextual data.  We think we can get away with this since the initial release will be a small subset of users.  When the time comes we will either write our own, or revisit the capabilities of CSLA’s authorization mechanism at that time.

Please don’t delay the writing of your book or finalization of CSLA 3.5 on our account; we would much prefer your book to get out sooner rather than later.

 

 



maurera replied on Tuesday, February 26, 2008

Sweet!!  I will let you know how we fair when we finally get back to this.  I suspect we won't revisit this issue until later on this year though at the rate we are moving.

JoeFallon1 replied on Wednesday, February 27, 2008

Rocky,

In BusinessBase the code to GetProperty always checks the Authorization rules first.

Question: If no rules are implemented is this check a performance penalty?

Joe

RockfordLhotka replied on Wednesday, February 27, 2008

I am sure there is some perf penalty. However, the result is cached, so it really only does the check once for a property in an object, and after that it continues to use that pre-determined result.

 

Without having a major explosion of overloads I am not sure how you’d prevent this – or prevent it in an efficient way. I suppose the PropertyInfo could include a flag indicating that no auth should occur – that’d be reasonable inexpensive.

 

Rocky

 

From: JoeFallon1 [mailto:cslanet@lhotka.net]
Sent: Wednesday, February 27, 2008 10:47 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: RE: CSLA .NET 3.5 enhancement - object level authorization

 

Rocky,

In BusinessBase the code to GetProperty always checks the Authorization rules first.

Question: If no rules are implemented is this check a performance penalty?

Joe



rsbaker0 replied on Thursday, February 21, 2008

RockfordLhotka:

...A lower level question is whether the user is in a specific "role" (or has a "permission" or whatever). This question is often required to answer the first question, but not always. The change in 3.5 is to allow you to "override" how this question is answered. This meets a very narrow need, where sometimes the question shouldn't be answered merely in terms of roles, but also in terms of permissions or other user profile data....

Ah, this would be perfect, if the new "override" just had the ability to inspect the object that prompted the question in the first place.

trives replied on Monday, February 11, 2008

I'm trying to leverage managed fields and the field manager (new in 3.5) to encapsulate in a base class object-level authorization for child objects.  For example, a method that I'll name CanUpdateChildObjects calls FieldManager.GetChildren in a recursive fashion to traverse an object graph and find all child objects.  An overload of the BusinessBase.Save method calls CanUpdateChildObjects.  For each new, deleted, or otherwise dirty child object in the graph, CanUpdateChildObjects calls the appropriate authorization method for the child's type (e.g. AuthorizationRules.CanEditObject(child.GetType()) ).   If a child is not dirty, then the method does not check authorization for that child type.

"Ditry" in this context, however, means something different than the IsDirty property.  Consider the following classes:

Project --> ProjectResources --> ProjectResource

Assume that a user doesn't have authorization to edit a Project (e.g. change the name, description, start date, end date, or add/delete project resources) but does have authorization to change a resource's role (i.e. can edit a ProjectResource).  If the user changes ProjectResource.Role, then Project.IsDirty retuns true because one of its child/grandchild objects is dirty.  When CanUpdateChildObjects checks authorization for the Project object, the check would fail causing an unintended  SecurityException.  In this context, "dirty" is the value of just _isDirty, with no consideration of child objects being dirty (i.e. not _isDirty || FieldManager.IsDirty).

This example is probably a bit contrived, but hopefully it gets the point across.  If the BusinessBase exposed something like an IsSelfDirty property, then that would solve the problem I think.  For example,

protected virtual bool IsSelfDirty
{
    get
    {
        return _isDirty;
    }
}

Also, a Saving event that exposed the IsDirty value would be useful in BusinessBase.  The call to CanUpdateChildObjects could occur in an override of OnSaving.  For example,

public virtual T Save()
{
    T result;
    if (this.IsChild)
        throw new NotSupportedException(Resources.NoSaveChildException);
    if (EditLevel > 0)
        throw new Validation.ValidationException(Resources.NoSaveEditingException);
    if (!IsValid && !IsDeleted)
        throw new Validation.ValidationException(Resources.NoSaveInvalidException);
    if (IsDirty)
    {
        OnSaving(true);
        result = (T)
DataPortal.Update(this);
    }
    else
    {
        OnSaving(false);
        result = (T)this;
    }
    OnSaved(result);
    return result;
}

An ideal solution would be to implement the checking of the child objects after the authorization check for the root object.  The root check happens in the client DataPortal, however, and I couldn't see how to make that work.  The DataPortalInvoke event didn't seem like a good place. 

I'm pretty sure that I can implement this in a custom base class using an _isSelfDirty field and overriding methods like MarkDirty, MarkOld, and MarkNew to maintain it.  But, the _isSelfDirty field would basically duplicate _isDirty.  So, if the suggestion of an IsSelfDirty property doesn't sound right for some reason  (e.g. maybe it's confusing or the usage too specific), that's not a deal breaker.  It seems like an innocuous change, so I thought I'd suggest it.

JoeFallon1 replied on Monday, February 11, 2008

You make an excelllent point!

I have been using a base class implementation for 4 years which is similar to what Rocky is adding to 3.5 as far as managing child objects and overriding IsDirty and IsValid.

Long ago I found the need to do exactly what you describe and I added a Property named IsDirtyOnly (also IsValidOnly) which just checks the root object without regard to the state of any children. This has proved extremely useful in many scenarios so I heartily recommend it for IsDirty and IsValid.

Joe

 

vdhant replied on Monday, February 11, 2008

Good points...
I think that there are some examples that one would need to know the difference between the object hierarchy being dirty and the object being dirty. I think that it would be a good idea to incorporate this concept into the framework.

Just my two bob worth.
Anthony

Just a thought, would you also want a IsSelfValid and IsSelfSavable?

skagen00 replied on Wednesday, February 13, 2008

I like the concept of an IsSelfDirty/IsSelfValid property - obviously this is something we can add downstream the base classes but it seems like it would be a fairly common need.

The hooks for OnSaving and OnSaved or what have you sound interesting to me. Right now to modify/inject behavior into the Save method you pretty much just have to override the whole thing in most cases.

Chris

SomeGuy replied on Wednesday, February 13, 2008

Doesn't calling base.IsDirty() accomplish the same thing? That is, checking the dirty status of the object irrespective of it's children?

E

 

trives replied on Wednesday, February 13, 2008

Calling base.IsDirty() calls the following code in Core.BusinessBase:

public virtual bool IsDirty
{
    get { return _isDirty || (_fieldManager != null && FieldManager.IsDirty()); }
}

It's true that the base implementation checks the object's _isDirty flag.  If it's false, however, it then calls FieldManager.IsDirty, which checks if any of the current object's registered properties are dirty plus calls IsDirty on any child objects.  So, the base implementation of IsDirty returns true if the current object is dirty or any of its child objects are dirty.

 

SomeGuy replied on Wednesday, February 13, 2008

Oops. I was thinking pre-FieldManager. Thanks.

E

 

trives replied on Friday, February 15, 2008

Hi Rocky,

I was wondering if you would consider using a provider pattern for object-level authorization.  For example, create an IObjectAuthorizationProvider interface:

public interface IObjectAuthorizationProvider
{
    void AllowGet(type objectType, params string[] roles);
    void DenyGet(type objectType, params string[] roles);
    void AllowCreate(type objectType, params string[] roles);
    void DenyCreate(type objectType, params string[] roles);
    void AllowDelete(type objectType, params string[] roles);
    void DenyDelete(type objectType, params string[] roles);
    void AllowEdit(type objectType, params string[] roles);
    void DenyEdit(type objectType, params string[] roles);
    bool CanCreateObject(type objectType);
    bool CanCreateObject(type objectType, object obj);
    bool CanDeleteObject(type objectType);
    bool CanDeleteObject(type objectType, object obj);
    bool CanEditObject(type objectType);
    bool CanEditObject(type objectType, object obj);
    bool CanGetObject(type objectType);
}

Then, in AuthorizationRules, move the implementation of the methods listed in the interface and the associated internal Get*Roles methods to another class (e.g. ObjectAuthorizationProvider).  This class would provide the default CSLA implementation.  So, the methods in AuthorizationRules related to object-level authorization would delegate to the provider.

What may look odd are the overloads of CanCreateObject, CanDeleteObject, CanEditObject that take a second parameter.  The second parameter is a reference to the root object.  I feel this is necessary because the state of an object and its children may affect authorization.  The DataPortal.Update would call CanCreateObject(type, obj) during an Insert, CanDeleteObject(type, obj) duing a deferred delete, and CanEditObject(type, obj) during an Execute or Update.  The other DataPortal methods would not call the new overloads.  The UI would likely call the original methods but could call any of them.

The reason for this suggestion is that I feel authorization can get pretty involved and can benefit from a good deal of flexibility.  Using a provider pattern like this shouldn't break anything for users of the existing authorization implementation and should allow application developers the flexibility to insert a custom authorization implementation.

 

JoeFallon1 replied on Friday, February 15, 2008

Rocky,

Please review this thread for the last few days. There are a number of points that require your feedback.

Thanks.

Joe

 

stefan replied on Saturday, February 16, 2008

To me it sounds like this is it...

not breaking 'traditional' code and
providing instance level authorization support for custom systems.

Stefan

RockfordLhotka replied on Thursday, February 21, 2008

I’ve thought about this at length, and I’m not going to do it in 3.5.

 

Partly this is due to time constraints – if I don’t finalize 3.5 very soon I won’t be able to start the Expert 2008 Business Objects book in time for it to be marketable, in which case the book won’t happen. We’re right at the edge of marketability as it is (projected completion is now out to September, which is terribly late).

 

Partly this is due to other people having suggested different approaches to solve the problem. I don’t have the time to do deep analysis/design on this right now, and it is the kind of thing that I could get wrong and then be forced into major breaking changes later to get right. That’d be bad.

 

So I think the best option is to stick with the current model in 3.5 and give the whole auth subsystem some serious redesign thinking for a future release – like 4.0 perhaps.

 

Rocky

 

 

From: trives [mailto:cslanet@lhotka.net]
Sent: Friday, February 15, 2008 12:43 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] CSLA .NET 3.5 enhancement - object level authorization

 

Hi Rocky,

I was wondering if you would consider using a provider pattern for object-level authorization.  For example, create an IObjectAuthorizationProvider interface:

public interface IObjectAuthorizationProvider
{
    void AllowGet(type objectType, params string[] roles);
    void DenyGet(type objectType, params string[] roles);
    void AllowCreate(type objectType, params string[] roles);
    void DenyCreate(type objectType, params string[] roles);
    void AllowDelete(type objectType, params string[] roles);
    void DenyDelete(type objectType, params string[] roles);
    void AllowEdit(type objectType, params string[] roles);
    void DenyEdit(type objectType, params string[] roles);
    bool CanCreateObject(type objectType);
    bool CanCreateObject(type objectType, object obj);
    bool CanDeleteObject(type objectType);
    bool CanDeleteObject(type objectType, object obj);
    bool CanEditObject(t ype objectType);
    bool CanEditObject(type objectType, object obj);
    bool CanGetObject(type objectType);
}

Then, in AuthorizationRules, move the implementation of the methods listed in the interface and the associated internal Get*Roles methods to another class (e.g. ObjectAuthorizationProvider).  This class would provide the default CSLA implementation.  So, the methods in AuthorizationRules related to object-level authorization would delegate to the provider.

What may look odd are the overloads of CanCreateObject, CanDeleteObject, CanEditObject that take a second parameter.  The second parameter is a reference to the root object.  I feel this is necessary because the state of an object and its children may affect authorization.  The DataPortal.Update would call CanCreateObject(type, obj) during an Insert, CanDeleteObject(type, obj) duing a deferred delete, and CanEditObject(type, obj) during an Execute or Update.  The other DataPortal methods would not call the new overloads.  The UI would likely call the original methods but could call any of them.

The reason for this suggestion is that I feel authorization can get pretty involved and can benefit from a good deal of flexibility.  Using a provider pattern like this shouldn't break anything for users of the existing authorization implementation and should allow application developers the flexibility to insert a custom authorization implementation.

 



akhirudin replied on Wednesday, May 14, 2008

hallo sir rocky

is this also available in version 2.1.4?

 

Regards

Fahmi

rcollette replied on Monday, May 04, 2009

Rocky,

I'm nearly through the book and I really appreciate your insight and efforts.

I too have encountered the need to authorize object creation based on some context beyond simply the current principal information.

For example, a user selects a company from a NameValue list (a root object).  The user may or may not be allowed to create new child objects for the selected company.  At this point, a Company object has not been loaded, its just a name value list.  I still want to be able to enable or disable the UI controls related to creating child objects.  

I definitely could see this being implemented as an instance method like:
RootObject.ChildObjectsListProperty.CanCreate()   (or maybe CanAdd() since it is a list method)

Assuming the ChildObjectList has a reference to its parent.

but there are times when instantiating the root object is overkill in terms of the data that needs to be loaded, in which case a static/shared method on the business object would be helpful:

Shared ChildObjectList.CanCreate(ContextualInfo as MyContext)

For example:

ClientAddressList.canCreate(clientId as integer)

or

Csla.Security.AuthorizationRules.CanCreateObject(typeof(ChildObjectList), ContextualInfo)

where AuthorizationRules delegates back to the type.

What I do want to avoid is the UI developer having to have some specific knowledge of which authorization method or overload of a method to call.   Which is why I have not moved forward with any custom security checks yet.

Since you're book has been out quite a while now, I am wondering if you have revisted this topic in your thoughts?

Thank you,
Rich Collette

RockfordLhotka replied on Tuesday, May 05, 2009

The per-type authz rules need to be implemented as static methods, because they operate at a type level, not an instance level.

As a result, they actually don't exist on any object - they exist as methods on the Csla.Security.AuthorizationRules class.

You are correct, I could add overloads to the methods to accept a second parameter of type object. The drawback to that is then the UI developer would need to know (via your documentation) what to actually pass into that parameter. The results wouldn't be self-documenting, and any errors could only be caught at runtime.

Maybe those issues can't be avoided - I'm just pointing out that they are there.

It would be better overall, I think, to not have such overloads, but rather for the authz method implementations to pick up the ambient context automatically. For example, the current implementations pick up the ambient user principal - that's the context. But other context is available through ApplicationContext in LocalContext, ClientContext and GlobalContext (among other locations).

Either way is doable though.

The basic model would be to change the existing implementation to use a provider model, where you are required to provide an object that implements some interface that checks the authz rules for all object types.

The existing CSLA implementation would be the default - where it just checks the roles. But you could provide CSLA with a replacement implementation that does whatever you'd like.

The hardest part of this is re-working some of the existing code in AuthorizationRules and AuthorizationRulesManager, because there are a number of non-public methods that a custom provider would need to use. And yet I don't want to make them public, because it would unecessarily complicate the public interface and could poke holes in the existing functionality.

In any case, there are a couple related items in the wish list

But I haven't done work on either of them. While the 2008 book has been done for a couple months, I've been doing a lot of other work for my employer, for Microsoft and on the CSLA .NET for Silverlight videos. And if you look, the wish list is quite long - these are just a couple items in a sea of requests :)

Finally, for better or worse, the next priority will almost certainly be Silverlight 3 support, immediately (or maybe concurrently) followed by .NET 4.0 support. Oh, and don't forget Azure support.

At least I never get bored ;)

Copyright (c) Marimer LLC