Override AddBusinessRules on Subclass

Override AddBusinessRules on Subclass

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


brianlagunas posted on Tuesday, May 03, 2011

It seems that it is not possible to have a subclass of a CSLA object such as:

[Serializable]
public abstract class GmcReadOnlyBase<T> : ReadOnlyBase<T> where T : GmcReadOnlyBase <T>
{
    
protected override void AddBusinessRules()
     {
         
base.AddBusinessRules();
          BusinessRules.AddRule(
new IsInRole(AuthorizationActions.CreateObject, SecurityRoles.SystemAdministrator));
          BusinessRules.AddRule(
new IsInRole(AuthorizationActions.DeleteObject, SecurityRoles.SystemAdministrator));
    
}

Then override the AddBusinessRules again to add more rules:

[Serializable]
public class CompanyInfo : GmcReadOnlyBase<CompanyInfo>
{
    
protected override void AddBusinessRules()
     {
         
base.AddBusinessRules();
          BusinessRules.AddRule(
new IsInRole(AuthorizationActions.CreateObject, SecurityRoles.Administrator));
          BusinessRules.AddRule(
new IsInRole(AuthorizationActions.DeleteObject, SecurityRoles.Administrator));
     }
 

The following appears in the output window: 

A first chance exception of type 'System.ArgumentException' occurred in Csla.dll
A first chance exception of type 'System.NullReferenceException' occurred in Csla.dll
A first chance exception of type 'Csla.Reflection.CallMethodException' occurred in Csla.dll
A first chance exception of type 'Csla.Server.DataPortalException' occurred in Csla.dll
A first chance exception of type 'Csla.Server.DataPortalException' occurred in Csla.dll
A first chance exception of type 'Csla.Server.DataPortalException' occurred in Csla.dll
A first chance exception of type 'Csla.DataPortalException' occurred in Csla.dll
A first chance exception of type 'Csla.DataPortalException' occurred in Csla.dll

Is there a way to declare global rules to the base object and then add to those rules in the derived object?

RockfordLhotka replied on Tuesday, May 03, 2011

Per-type rules are set in the static AddObjectAuthorizationRules method:

    protected static void AddObjectAuthorizationRules()
    {
      Csla.Rules.BusinessRules.AddRule(typeof(ProjectEdit), 
        new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.CreateObject, "ProjectManager"));
      Csla.Rules.BusinessRules.AddRule(typeof(ProjectEdit), 
        new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.EditObject, "ProjectManager"));
      Csla.Rules.BusinessRules.AddRule(typeof(ProjectEdit), 
        new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.DeleteObject, "ProjectManager""Administrator"));
    }

This is discussed in the Using CSLA 4: Creating Business Objects ebook.

The rules are set per-type, and the data portal only operates on the concrete (non-generic) type of a specific business class. So the answer is no, you can't establish per-type rules at a base type level and have them apply to subclasses.

brianlagunas replied on Tuesday, May 03, 2011

Thanks for the quick response. This is offically the first limitation I have had with CSLA.  Not a big one though, I will just have to add the role to every object instead.  I guess I should buy your new book :0)

sergeyb replied on Tuesday, May 03, 2011

Would this work in CustomBusinessBase<T> : BusinessBase<T>  class?

 

    protected static void AddObjectAuthorizationRules()
    {
      Csla.Rules.BusinessRules.AddRule(typeof(T), 
        new Csla.Rules.CommonRules.IsInRole(Csla.Rules.AuthorizationActions.CreateObject, "ProjectManager"));
    }

RockfordLhotka replied on Tuesday, May 03, 2011

That's an interesting thought Sergey, but I don't think CSLA will find or invoke that AddObjectAuthorizationRules method, because it isn't on the concrete type.

sergeyb replied on Tuesday, May 03, 2011

I must be thinking 3.8 then.  I think that version used to invoke AddObjectAuthorizationRules method when evaluating rules for a type in ObjectAuthorizationRules class, so I was thinking it would find the method on base type and evaluate T properly...  Would be cool if it did...  I like global rules a lot...

brianlagunas replied on Tuesday, May 03, 2011

This in fact does work, but the problem is that this method is static, and I can't "add" rules to it from the derived class.  The call to AddObjectAuthorizationRules in the derived class would hide the call to the base class.

sergeyb replied on Tuesday, May 03, 2011

I wonder if you could make this method smarter, and walk inheritance change (while currentType.BaseType !+ null) and invoke this method using reflection on each type?

Seems kind of ugly at a high level though...

JonnyBee replied on Tuesday, May 03, 2011

No way that will work with our current codebase in CSLA 4.

AuthorizationRules will only allow one - 1 rule per AuthorizationAction / IElementInfo.

You cannot have 2 Authz rules instances for f.ex  AuthorizationActions.CreateObject or AuthorizationActions.DeleteObject like the sample that Brian has in his original post.

Which is why you should only define (at least the static)  Authz rule at the lowest level.

 

rxelizondo replied on Tuesday, May 03, 2011

Brian Lagunas

This in fact does work, but the problem is that this method is static, and I can't "add" rules to it from the derived class.  The call to AddObjectAuthorizationRules in the derived class would hide the call to the base class.

 

 

     protected static void AddObjectAuthorizationRules()

     {

          // Calls base class static method.

          GmcReadOnlyBase<CompanyInfo>.AddBusinessRules();

          ........

     } 

 

brianlagunas replied on Tuesday, May 03, 2011

Yes, I am aware I could make that call, but it doesn't solve the problem which JohnnyBee puts it best. In a nutshell, CSLA does not support inherited Auth rules.

RockfordLhotka replied on Tuesday, May 03, 2011

I think Rene's answer is a decent workaround though?

Implement the default behavior in the base static method, and invoke that from your concrete class - unless you want custom rules in the concrete class, in which case you wouldn't invoke the base method because you are creating custom rules for the specific type.

brianlagunas replied on Tuesday, May 03, 2011

The reason for this behavior is for this scenario.  Let's say that I have a few security roles one of them being a SystemAdmin.  Naturally SystemAdmin will have rights to every single object, where other roles will have limited rights.  The intent was not to have to keep adding the same SystemAdmin role to every single object, but instead add it to a base object so every single object will have SystemAdmin rights applied.  Now all derived objects will have more roles appended to the object such as ProjectManager or whatever.  The idea is to append, or add additional rights to the object.  I hope I am being clear.

RockfordLhotka replied on Tuesday, May 03, 2011

I see what you are saying.

I wonder if a variation on Rene's idea wouldn't work though?

Suppose your base class implemented a static method such as AddObjectAuthorizationRule(action, roleList)?

This method would be invoked from the AddObjectAuthorizationRules method of each concrete class, and would append any required roles (such as SystemAdmin) to the list of roles provided by the concrete class. At least each concrete class would only deal with its specific roles, leaving global roles to the base class.

JonnyBee replied on Tuesday, May 03, 2011

That is absolutely doable for Object level authorization rules.

Just be aware that the same restrictions (only 1 - one aAuthz rule) also applies to methods and properties. So you may need to create a similar implementation for Authz in AddBusinessRules.

That may also force you to override CanReadProperty / CanWriteProperty /CanExecuteMethod for "business-type" related Authz rules as you can never know if any subclass / baseclass would add an Autz rule to the field. I'm thinking of rules like this field is required based on another field value but only if you are Admin.

Copyright (c) Marimer LLC