CSLA .NET

Vibrant discussion about CSLA .NET and using the framework to build great business applications.

Business Rule for Not Allowing Edit or Delete for a Specific Record

Answered (Not Verified) This post has 0 verified answers | 22 Replies | 2 Followers

Not Ranked
3 Posts
korzy1bj@gmail.com posted on Thu, Jan 19 2012 10:26 AM

I have a business object and a corresponding database table called Role. In this table I am preloading two default records. The first is named "Administrators" and second is named "Users". I want to allow people to add, edit, and delete roles, except for these default ones. I was wondering how I should go about this. What I was thinking was to create a business rule, but I’m not sure how to add one to handle this particular situation. Any help / advice you can provide would be greatly appreciated.

P.S. I have a business rule to not allow them to enter duplicate names, so I don’t have to worry about them creating another “Administrators” role.

All Replies

Top 10 Contributor
1,815 Posts
Answered (Not Verified) JonnyBee replied on Thu, Jan 19 2012 11:49 AM
Suggested by JonnyBee

Hi,

I'd recommend to add a flag (bool) to the Roles table that defines IsSystem.

You can then add an Authorization rule that can prevent delete/edit when a Role has IsSystem == true.

To prevent edit of field you should probably just override CanWriteProperty like this:

 

    public static readonly PropertyInfo<bool> IsSystemProperty = RegisterProperty<bool>(c => c.IsSystem);
    public bool IsSystem
    {
      get { return GetProperty(IsSystemProperty); }
      set { SetProperty(IsSystemProperty, value); }
    }
 
    public override bool CanWriteProperty(Csla.Core.IPropertyInfo property)
    {
      if (IsSystem) 
return false;       return base.CanWriteProperty(property);     }
or you could create an authz rule that checks the IsSystem flag and override CanWriteProperty:
    public override bool CanWriteProperty(IPropertyInfo propertyName)
    {
      if (!Csla.Rules.BusinessRules.HasPermission(AuthorizationActions.EditObject, this))
        return false;
      return base.CanWriteProperty(property);
    }

Jonny Bekkum, Norway CslaContrib Coordinator

Not Ranked
3 Posts

Thank you very much for your help and for your quick response. I will try this out today.

Not Ranked
3 Posts

Could you show me what the authorization rule would like like for preventing them from deleting and editing a role that has IsSystem == true?

Top 10 Contributor
1,815 Posts
JonnyBee replied on Wed, Jan 25 2012 11:18 AM

Pretty muuch like this:

  public class IsSystem : Csla.Rules.AuthorizationRule
  {
 
    private IPropertyInfo SystemField { getset; }
 
    public IsSystem(AuthorizationActions action, IMemberInfo element, IPropertyInfo systemField)
      : base(action, element)
    {
      SystemField = systemField;
    }
 
    public override bool CacheResult
    {
      get
      {
        return false;
      }
    }
 
    protected override void Execute(AuthorizationContext context)
    {
      if (context.Target == null)
      {
        context.HasPermission = true;
        return;
      }
 
      //var isSystem = (bool)MethodCaller.CallPropertyGetter(context.Target, SystemField.Name);
      var isSystem = (bool) ReadProperty(context.Target, SystemField);
      context.HasPermission = !isSystem;
    }
  }

Jonny Bekkum, Norway CslaContrib Coordinator

Top 200 Contributor
46 Posts
mattruma replied on Wed, Jan 25 2012 11:34 AM

My implementation usually intercepts the delete function.

        public override void Delete()
        {
            if (this.IsSystem)
            {
                throw new InvalidOperationException(Resources.SystemDefinedObjectCannotBeDeletedMessage);
            }
 
            base.Delete();
        }
 
        public static void DeleteRoleEdit(int roleId)
        {
            if (RoleIsSystemCommand.RoleIsSystem(roleId))
            {
                throw new InvalidOperationException(Resources.SystemDefinedObjectCannotBeDeletedMessage);
            }
 
            RoleEdit.DeleteRoleEdit(new RoleDataCriteria { RoleId = roleId });
        }

Not sure if this is the better way to do it with CSLA.

Top 200 Contributor
46 Posts

When wiring this rule up ... would the value passed for element be null?

Top 50 Contributor
184 Posts

Hi JonnyBee, I'm trying to implement this authorization rule and it's giving me trouble.  Apparently it's in the name of the rule or something.

 After the return from the constructor I consistently get an exception:

System.ArgumentException with the only message being "rule" 

   at Csla.Rules.BusinessRules.EnsureUniqueRule(AuthorizationRuleManager mgr, IAuthorizationRule rule)

   at Csla.Rules.BusinessRules.AddRule(IAuthorizationRule rule)

   at MyApp.Library.PartWarehouse.AddBusinessRules() in C:\data\an\MyApp.Library\PartWarehouse.cs:line 262

   at Csla.Core.BusinessBase.InitializeBusinessRules()

 

It's called from here:

        public static void AddObjectAuthorizationRules()

        { 

            Csla.Rules.BusinessRules.AddRule(typeof(PartWarehouse), new Admin.IsAllowed(Csla.Rules.AuthorizationActions.GetObject, RootObjectTypes.PartWarehouse));

            Csla.Rules.BusinessRules.AddRule(typeof(PartWarehouse), new Admin.IsAllowed(Csla.Rules.AuthorizationActions.CreateObject, RootObjectTypes.PartWarehouse));

            Csla.Rules.BusinessRules.AddRule(typeof(PartWarehouse), new Admin.IsAllowed(Csla.Rules.AuthorizationActions.EditObject, RootObjectTypes.PartWarehouse));

            Csla.Rules.BusinessRules.AddRule(typeof(PartWarehouse), new Admin.IsAllowed(Csla.Rules.AuthorizationActions.DeleteObject, RootObjectTypes.PartWarehouse));

            Csla.Rules.BusinessRules.AddRule(typeof(PartWarehouse), new Admin.IsSystemObjectRule(Csla.Rules.AuthorizationActions.DeleteObject, IsDefaultWarehouseProperty));

        }

Is it necessary to set a Rule URI or something manually?

 

Also here is the complete rule which is, I believe, identical to yours with minor exception:

using System;

using Csla.Rules;

using Csla.Core;

namespace MyApp.Library.Admin

{

    public class IsSystemObjectRule : Csla.Rules.AuthorizationRule

    {

        private IPropertyInfo IsSystemObjectField { get; set; }

 

        public IsSystemObjectRule(AuthorizationActions action, IPropertyInfo isSystemObjectField) : base(action)

        {

            IsSystemObjectField = isSystemObjectField;

        }

 

        public override bool CacheResult

        {

            get

            {

                return false;

            }

        }       

        protected override void Execute(AuthorizationContext context)

        {

            if(context.Target == null)

            {

                context.HasPermission = true;

                return;

            }

            var isSystem = (bool)ReadProperty(context.Target, IsSystemObjectField);

            context.HasPermission = !isSystem;                     

        }

    }//eoc       

}

Any help would be greatly appreciated

Top 10 Contributor
1,815 Posts
Suggested by JonnyBee

This is a limitation in the rule engine.
There can only be one AuthzRule per static action or per action/property.

IE: The RuleEngine do NOT support having a "set" of AuthzRules as is the case for BusinessRules. 

Your only option here is to add more properties to the same rule instance. 

Jonny Bekkum, Norway CslaContrib Coordinator

Top 50 Contributor
184 Posts

Ahh...thanks Jonny, that clears it up.

Perhaps the error message: "rule" could be improved upon? :)

Top 50 Contributor
184 Posts

JonnyBee:
Your only option here is to add more properties to the same rule instance. 

Hi Jonny, I've been playing with it and I can't figure out how to do that.  In fact I thought I *did* do that in my sample:

BusinessRules.AddRule(new Admin.IsSystemObjectRule(Csla.Rules.AuthorizationActions.DeleteObject, IsDefaultWarehouseProperty));

Could you please tell me what I'm missing here?

Top 50 Contributor
184 Posts

Aha! Figured it out, sorry Jonny, I didn't dig enough.  Thanks for your help.

For any future people wondering you need to pass a property to the base constructor for the authorization rule, like this based on my code above (change in bold):

public IsSystemObjectRule( AuthorizationActions action, IPropertyInfo isSystemObjectField)
            : base(action,isSystemObjectField)

Top 50 Contributor
184 Posts

Well, I got the rule working in that it's constructor is called and it doesn't blow up with an exception.

However the rules' execute method is *never* called.  Is this because I've had to specify a property to make it unique and now it's not tied to the delete operation any more but only if that property get's modified?

 

Top 10 Contributor
1,815 Posts

As I stated in my previous post the rule engine only supports one - 1 rule per action for any check.

While you may add more rules - there will only be executed one-1 rule for each Authz action.

Jonny Bekkum, Norway CslaContrib Coordinator

Top 50 Contributor
184 Posts

Oh, well that's a problem then. Smile

I thought if the bogus property was added it became unique and in addition to no longer throwing the cryptic exception would execute.

So what you're saying is that you can not write an authorization rule to do what the topic of this thread is and still have regular authorization rules for security?  Or we'd need some kind of really complex hybrid single authorization rule that handled not only the security role case but also the single instance state prevents deletion case.

Page 1 of 2 (23 items) 1 2 Next > | RSS

Copyright (c) 2006-2010 Marimer LLC. All rights reserved.
Email admin@lhotka.net for support.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems