9using System.Collections;
11using System.Collections.Generic;
14using System.Threading.Tasks;
16using System.Security.Principal;
45 private object SyncRoot =
new();
51 private BrokenRulesCollection _brokenRules;
52 private BrokenRulesCollection BrokenRules
56 if (_brokenRules ==
null)
57 _brokenRules =
new BrokenRulesCollection(
true);
62 private bool _suppressRuleChecking;
71 get {
return _suppressRuleChecking; }
72 set { _suppressRuleChecking = value; }
75 private int _processThroughPriority;
82 get {
return _processThroughPriority; }
83 set { _processThroughPriority = value; }
86 private string _ruleSet =
null;
97 _typeAuthRules =
null;
99 if (BrokenRules.Count > 0)
101 BrokenRules.ClearRules();
114 get {
return _cascadeOnDirtyProperties; }
115 set { _cascadeOnDirtyProperties = value; }
125 if (_typeRules ==
null && _target !=
null)
132 private AuthorizationRuleManager _typeAuthRules;
133 internal AuthorizationRuleManager TypeAuthRules
137 if (_typeAuthRules ==
null && _target !=
null)
138 _typeAuthRules = AuthorizationRuleManager.GetRulesForType(ApplicationContext, _target.GetType(), _ruleSet);
139 return _typeAuthRules;
150 var result =
new List<string>();
151 foreach (var item
in TypeRules.
Rules)
152 result.Add(item.RuleName);
153 return result.ToArray();
165 internal object Target
167 get {
return _target; }
176 TypeRules.
Rules.Add(rule);
187 typeRules.
Rules.Add(rule);
196 EnsureUniqueRule(TypeAuthRules, rule);
197 TypeAuthRules.
Rules.Add(rule);
220 AddRule(
null, objectType, rule, ruleSet);
234 EnsureUniqueRule(typeRules, rule);
235 typeRules.Rules.Add(rule);
242 oldRule = mgr.
Rules.FirstOrDefault(c => c.Element !=
null && c.Element.Name == rule.
Element.
Name && c.Action == rule.
Action);
244 oldRule = mgr.
Rules.FirstOrDefault(c => c.Element ==
null && c.Action == rule.
Action);
246 throw new ArgumentException(nameof(rule));
268 private bool _runningRules;
275 get {
return _runningRules; }
276 private set { _runningRules = value; }
280 private bool _isBusy;
288 if (_busyChanged ==
null)
300 get {
return _isBusy; }
318 return BusyProperties.Contains(property);
329 if (applicationContext ==
null)
330 throw new ArgumentNullException(nameof(applicationContext));
332 return HasPermission(action,
null, applicationContext, objectType,
null, applicationContext.
RuleSet);
344 if (applicationContext ==
null)
345 throw new ArgumentNullException(nameof(applicationContext));
347 return HasPermission(action,
null, applicationContext, objectType, criteria, applicationContext.
RuleSet);
362 if (applicationContext ==
null)
363 throw new ArgumentNullException(nameof(applicationContext));
364 return HasPermission(action,
null, applicationContext, objectType,
null, ruleSet);
375 if (applicationContext ==
null)
376 throw new ArgumentNullException(nameof(applicationContext));
377 return HasPermission(action, obj, applicationContext, obj.GetType(),
null, applicationContext.
RuleSet);
386 throw new ArgumentOutOfRangeException($
"{nameof(action)}, {action}");
390 AuthorizationRuleManager.GetRulesForType(applicationContext, objType, ruleSet).
Rules.FirstOrDefault(c => c.Element ==
null && c.Action == action);
393 var context =
new AuthorizationContext(applicationContext, rule, obj, objType) { Criteria = criteria };
394 rule.Execute(context);
408 if (_suppressRuleChecking)
415 throw new ArgumentOutOfRangeException($
"{nameof(action)}, {action}");
419 TypeAuthRules.
Rules.FirstOrDefault(c => c.Element !=
null && c.Element.Name == element.Name && c.Action == action);
422 var context =
new AuthorizationContext(applicationContext, rule, this.Target, this.Target.GetType());
423 rule.Execute(context);
424 result = context.HasPermission;
438 if (_suppressRuleChecking)
443 TypeAuthRules.
Rules.FirstOrDefault(c => c.Element !=
null && c.Element.Name == element.
Name && c.Action == action);
445 result = rule.CacheResult;
463 var tasks =
new Task[] { BusyChanged.
WaitAsync(), Task.Delay(timeout) };
464 var
final = await Task.WhenAny(tasks);
465 if (
final == tasks[1])
494 if (_suppressRuleChecking)
495 return new List<string>();
499 var properties = TypeRules.
Rules.Where(p => p.PrimaryProperty !=
null)
500 .Select(p => p.PrimaryProperty)
502 foreach (var property
in properties)
507 return affectedProperties.Distinct().ToList();
536 if (_suppressRuleChecking)
537 return new List<string>();
541 var rules = from r in TypeRules.
Rules
542 where r.PrimaryProperty ==
null
546 BrokenRules.ClearRules(
null);
548 var firstResult = RunRules(rules,
false, executionContext);
554 foreach (var item
in rules)
557 foreach (var p
in item.AffectedProperties)
558 propertiesToRun.Add(p);
561 foreach (var item
in propertiesToRun.Distinct())
563 var doCascade =
false;
565 doCascade = firstResult.DirtyProperties.Any(p => p == item.Name);
566 firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade,
574 return firstResult.AffectedProperties.Distinct().ToList();
594 if (property ==
null)
595 throw new ArgumentNullException(nameof(property));
597 if (_suppressRuleChecking)
598 return new List<string>();
603 var affectedProperties =
new List<string>();
604 affectedProperties.AddRange(CheckRulesForProperty(property,
true, executionContext));
609 return affectedProperties.Distinct().ToList();
624 if (rule.RunMode ==
RunModes.Default)
return true;
629 canRun &= (rule.RunMode &
RunModes.DenyAsAffectedProperty) == 0;
631 if ((rule.RunMode &
RunModes.DenyOnServerSidePortal) > 0)
635 canRun &= (rule.RunMode &
RunModes.DenyCheckRules) == 0;
649 var rules = from r in TypeRules.
Rules
650 where ReferenceEquals(r.PrimaryProperty, property)
651 && CanRunRule(ApplicationContext, r, executionContext)
655 BrokenRules.ClearRules(property);
656 var firstResult = RunRules(rules, cascade, executionContext);
658 cascade = cascade || firstResult.DirtyProperties.Any();
663 foreach (var item
in rules)
666 foreach (var p
in item.AffectedProperties)
667 if (!ReferenceEquals(property, p))
668 propertiesToRun.Add(p);
672 var input = from r in TypeRules.
Rules
673 where !ReferenceEquals(r.PrimaryProperty, property)
674 && r.PrimaryProperty !=
null
675 && r.InputProperties !=
null
676 && r.InputProperties.Contains(property)
677 select r.PrimaryProperty;
679 foreach (var p
in input)
681 if (!ReferenceEquals(property, p))
682 propertiesToRun.Add(p);
685 foreach (var item
in propertiesToRun.Distinct())
687 var doCascade =
false;
689 doCascade = firstResult.DirtyProperties.Any(p => p == item.Name);
690 firstResult.AffectedProperties.AddRange(CheckRulesForProperty(item, doCascade,
696 firstResult.AffectedProperties.Add(property.Name);
697 return firstResult.AffectedProperties.Distinct().ToList();
703 private bool _cascadeOnDirtyProperties;
709 if (_busyProperties ==
null)
711 return _busyProperties;
722 private RunRulesResult RunRules(IEnumerable<IBusinessRuleBase> rules,
bool cascade,
RuleContextModes executionContext)
724 var affectedProperties =
new List<string>();
725 var dirtyProperties =
new List<string>();
726 bool anyRuleBroken =
false;
727 foreach (var rule
in rules)
732 bool complete =
false;
734 var context =
new RuleContext(ApplicationContext, (r) =>
741 if (r.OutputPropertyValues != null)
742 foreach (var item in r.OutputPropertyValues)
745 if (((IManageProperties)_target).LoadPropertyMarkDirty(item.Key, item.Value))
746 r.AddDirtyProperty(item.Key);
749 BrokenRules.SetBrokenRules(r.Results, r.OriginPropertyName);
752 var affected = new List<string>();
754 foreach (var item in r.Rule.AffectedProperties.Distinct())
755 if (!ReferenceEquals(r.Rule.PrimaryProperty, item))
757 var doCascade = false;
758 if (CascadeOnDirtyProperties && (r.DirtyProperties != null))
759 doCascade = r.DirtyProperties.Any(p => p.Name == item.Name);
760 affected.AddRange(CheckRulesForProperty(item, doCascade, r.ExecuteContext | RuleContextModes.AsAffectedPoperty));
764 foreach (var item in r.Rule.AffectedProperties)
766 BusyProperties.Remove(item);
767 RunningAsyncRules = BusyProperties.Count > 0;
768 if (!BusyProperties.Contains(item))
769 _target.RuleComplete(item);
772 foreach (var property in affected.Distinct())
775 if (!r.Rule.AffectedProperties.Any(p => p.Name == property))
776 _target.RuleComplete(property);
779 if (!RunningRules && !RunningAsyncRules)
780 _target.AllRulesComplete();
786 if (r.OutputPropertyValues !=
null)
787 foreach (var item in r.OutputPropertyValues)
790 if (((IManageProperties)_target).LoadPropertyMarkDirty(item.Key, item.Value))
791 r.AddDirtyProperty(item.Key);
795 if (r.Results !=
null)
797 BrokenRules.SetBrokenRules(r.Results, r.OriginPropertyName);
800 if (r.Results.Any(p => !p.Success && p.Severity == RuleSeverity.Error))
801 anyRuleBroken = true;
810 if (rule.PrimaryProperty !=
null)
811 context.OriginPropertyName = rule.PrimaryProperty.Name;
812 context.ExecuteContext = executionContext;
813 if (!rule.IsAsync || rule.ProvideTargetWhenAsync)
814 context.Target = _target;
817 if (rule.InputProperties !=
null)
819 var target = (IManageProperties) _target;
820 context.InputPropertyValues =
new Dictionary<IPropertyInfo, object>();
821 foreach (var item
in rule.InputProperties)
826 if (target.FieldExists(item))
827 context.InputPropertyValues.Add(item, target.ReadProperty(item));
830 context.InputPropertyValues.Add(item, target.ReadProperty(item));
840 foreach (var item
in rule.AffectedProperties)
842 var alreadyBusy = BusyProperties.Contains(item);
843 BusyProperties.Add(item);
854 if (rule is IBusinessRule syncRule)
855 syncRule.Execute(context);
856 else if (rule is IBusinessRuleAsync asyncRule)
857 RunAsyncRule(asyncRule, context);
859 throw new ArgumentOutOfRangeException(rule.GetType().FullName);
863 context.AddErrorResult(
string.Format(
"{0}:{1}", rule.RuleName, ex.Message));
874 affectedProperties.AddRange(rule.AffectedProperties.Select(c => c.Name));
876 if (context.OutputPropertyValues !=
null)
877 affectedProperties.AddRange(context.OutputPropertyValues.Select(c =>c.Key.Name));
879 if (context.DirtyProperties !=
null)
880 dirtyProperties.AddRange(context.DirtyProperties.Select(c => c.Name));
882 if (context.Results !=
null)
885 if (context.Results.Any(r => r.StopProcessing))
891 return new RunRulesResult(affectedProperties, dirtyProperties);
894 private static async
void RunAsyncRule(IBusinessRuleAsync asyncRule, IRuleContext context)
898 await asyncRule.ExecuteAsync(context);
906 #region DataAnnotations
912 [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
916#if !NETSTANDARD2_0 && !NET5_0 && !NET6_0
918 var classAttList = _target.GetType().GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.MetadataTypeAttribute),
true);
919 if (classAttList.Length > 0)
921 metadataType = ((System.ComponentModel.DataAnnotations.MetadataTypeAttribute)classAttList[0]).MetadataClassType;
922 AddDataAnnotationsFromType(metadataType);
927 metadataType = _target.GetType();
928 AddDataAnnotationsFromType(metadataType);
935 private void AddDataAnnotationsFromType(Type metadataType)
937 var attList = metadataType.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.ValidationAttribute),
true);
938 foreach (var att
in attList)
939 AddRule(
new CommonRules.DataAnnotation(
null, (System.ComponentModel.DataAnnotations.ValidationAttribute)att));
942 var propList = metadataType.GetProperties();
943 foreach (var prop
in propList)
945 attList = prop.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.ValidationAttribute),
true);
946 foreach (var att
in attList)
948 var target = (IManageProperties)_target;
949 var pi = target.GetManagedProperties().First(c => c.Name == prop.Name);
950 AddRule(
new CommonRules.DataAnnotation(pi, (System.ComponentModel.DataAnnotations.ValidationAttribute)att));
957 #region MobileObject overrides
971 info.
AddValue(
"_processThroughPriority", _processThroughPriority);
972 info.
AddValue(
"_ruleSet", _ruleSet);
973 info.
AddValue(
"_cascadeWhenChanged", _cascadeOnDirtyProperties);
974 base.OnGetState(info, mode);
989 _processThroughPriority = info.GetValue<
int>(
"_processThroughPriority");
990 _ruleSet = info.GetValue<
string>(
"_ruleSet");
991 _cascadeOnDirtyProperties = info.GetValue<
bool>(
"_cascadeWhenChanged");
992 base.OnSetState(info, mode);
1008 if (_brokenRules !=
null && _brokenRules.Count > 0)
1014 base.OnGetChildren(info, formatter);
1030 if (info.
Children.ContainsKey(
"_brokenRules"))
1032 int referenceId = info.
Children[
"_brokenRules"].ReferenceId;
1036 base.OnSetChildren(info, formatter);
1040 #region Serialization Notification
1044 OnDeserializedHandler(
new System.Runtime.Serialization.StreamingContext());
1047 [System.Runtime.Serialization.OnDeserialized]
1048 private void OnDeserializedHandler(System.Runtime.Serialization.StreamingContext context)
1050 SyncRoot =
new object();
1055 #region Get All Broken Rules (tree)
1064 return GetAllBrokenRules(root,
true);
1076 long childBrokenRuleCount = 0;
1077 AddNodeToBrukenRules(ref list, ref counter,
null, root, errorsOnly, ref childBrokenRuleCount);
1082 private static void AddNodeToBrukenRules(ref
BrokenRulesTree list, ref
long counter,
object parentKey,
object obj,
bool errorsOnly, ref
long childBrokenRuleCount)
1087 var nodeKey = counter++;
1089 long myChildBrokenRuleCount = bo.BrokenRulesCollection.Count;
1090 var node =
new BrokenRulesNode() { Parent = parentKey, Node = nodeKey, BrokenRules = bo.BrokenRulesCollection, Object = obj };
1094 foreach (var child
in ((IManageProperties)bo).GetChildren())
1096 AddNodeToBrukenRules(ref list, ref counter, nodeKey, child, errorsOnly, ref myChildBrokenRuleCount);
1100 if (!errorsOnly && myChildBrokenRuleCount == 0)
1104 if (errorsOnly && bo.IsValid)
1108 childBrokenRuleCount += myChildBrokenRuleCount;
1114 var nodeKey = counter++;
1116 var node =
new BrokenRulesNode() { Parent = parentKey, Node = nodeKey, Object = obj, BrokenRules =
new BrokenRulesCollection(
true) };
1117 long myChildBrokenRuleCount = 0;
1121 foreach (var child
in (IEnumerable)obj)
1123 AddNodeToBrukenRules(ref list, ref counter, nodeKey, child, errorsOnly, ref myChildBrokenRuleCount);
1127 if (!errorsOnly && myChildBrokenRuleCount == 0)
1131 if (errorsOnly && isValid)
1135 childBrokenRuleCount += myChildBrokenRuleCount;
1143 internal class RunRulesResult
1145 public RunRulesResult(List<string> affectedProperties, List<string> dirtyProperties)
1147 AffectedProperties = affectedProperties;
1148 DirtyProperties = dirtyProperties;
1151 public List<string> AffectedProperties {
get;
set; }
1152 public List<string> DirtyProperties {
get;
set; }
1155 object IBusinessRules.Target
1157 get {
return Target; }
Provides consistent context information between the client and server DataPortal objects.
LogicalExecutionLocations LogicalExecutionLocation
Return Logical Execution Location - Client or Server This is applicable to Local mode as well
LogicalExecutionLocations
Enum representing the logical execution location The setting is set to server when server is execting...
string? RuleSet
Gets or sets the RuleSet name to use for static HasPermission calls.
const string DefaultRuleSet
The default RuleSet name
This is the non-generic base class from which most business objects will be derived.
Inherit from this base class to easily create a serializable class.
Context information provided to an authorization rule when it is invoked.
bool HasPermission
Gets or sets a value indicating whether the current user has permission to perform the requested acti...
Manages the list of authorization rules for a business type.
List< IAuthorizationRule > Rules
Gets the list of rule objects for the business type.
A collection of currently broken rules.
int ErrorCount
Gets the number of broken rules in the collection that have a severity of Error.
Holds broken rules for an Node in the BrokenRulesTree.
Holds a list of broken rules tree list
Manages the list of rules for a business type.
List< IBusinessRuleBase > Rules
Gets the list of rule objects for the business type.
Tracks the business rules for a business object.
BusinessRules()
Creates an instance of the type.
string?? RuleSet
Gets or sets the rule set to use for this business object instance.
async Task< List< string > > CheckRulesAsync()
Invokes all rules for the business type.
int ProcessThroughPriority
Gets or sets the priority through which all rules will be processed.
List< string > CheckRules()
Invokes all rules for the business type.
override void OnSetChildren(SerializationInfo info, MobileFormatter formatter)
Override this method to retrieve your child object references from the MobileFormatter serialzation s...
static BrokenRulesTree GetAllBrokenRules(object root, bool errorsOnly)
Gets all nodes in tree that have broken rules.
static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Type objectType, object[] criteria)
Checks per-type authorization rules.
async Task< List< string > > CheckRulesAsync(int timeout)
Invokes all rules for the business type.
static void AddRule(ApplicationContext applicationContext, Type objectType, IAuthorizationRule rule, string ruleSet)
Associates a per-type authorization rule with the business type.
bool SuppressRuleChecking
Gets or sets a value indicating whether calling CheckRules should result in rule methods being invoke...
bool IsValid
Gets a value indicating whether there are any currently broken rules, which would mean the object is ...
static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, object obj)
Checks per-instance authorization rules.
void AddRule(IAuthorizationRule rule)
Associates an authorization rule with the business object.
BusinessRules(ApplicationContext applicationContext, IHostRules target)
Creates an instance of the type.
void AddRule(IBusinessRuleBase rule, string ruleSet)
Associates a business rule with the business object.
bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Csla.Core.IMemberInfo element)
Checks per-property authorization rules.
bool RunningRules
Gets a value indicating whether a CheckRules operation is in progress.
bool CascadeOnDirtyProperties
Gets or sets a value indicating whether rule engine should cacade n-leves when property value is chan...
List< string > CheckObjectRules()
Invokes all rules attached at the class level of the business type.
static void AddRule(Type objectType, IAuthorizationRule rule, string ruleSet)
Associates a per-type authorization rule with the business type.
static BrokenRulesTree GetAllBrokenRules(object root)
Gets all nodes in tree that have IsValid = false (and all parents)
void AddDataAnnotations()
Adds validation rules corresponding to property data annotation attributes.
override void OnGetState(SerializationInfo info, StateMode mode)
Override this method to insert your field values into the MobileFormatter serialzation stream.
List< string > CheckRules(Csla.Core.IPropertyInfo property)
Invokes all rules for a specific property of the business type.
static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Type objectType)
Checks per-type authorization rules.
bool GetPropertyBusy(Csla.Core.IPropertyInfo property)
Gets a value indicating whether a specific property has any async rules running.
void AddRule(IBusinessRuleBase rule)
Associates a business rule with the business object.
bool RunningAsyncRules
Gets a value indicating whether any async rules are currently executing.
override void OnSetState(SerializationInfo info, StateMode mode)
Override this method to retrieve your field values from the MobileFormatter serialzation stream.
bool CachePermissionResult(AuthorizationActions action, IMemberInfo element)
Gets a value indicating whether the permission result can be cached.
static void AddRule(Type objectType, IAuthorizationRule rule)
Associates a per-type authorization rule with the business type in the default rule set.
BrokenRulesCollection GetBrokenRules()
Gets the broken rules list.
string[] GetRuleDescriptions()
Gets a list of rule:// URI values for the rules defined in the object.
static bool HasPermission(ApplicationContext applicationContext, AuthorizationActions action, Type objectType, string ruleSet)
Checks per-type authorization rules.
override void OnGetChildren(SerializationInfo info, MobileFormatter formatter)
Override this method to insert your child object references into the MobileFormatter serialzation str...
Object containing the serialization data for a specific object.
int ReferenceId
Reference number for this object.
Dictionary< string, ChildData > Children
Dictionary containing child reference data.
void AddChild(string name, int referenceId)
Adds a child to the list of child references.
void AddValue(string name, object value)
Adds a value to the serialization stream.
Async/await implementation of a ManualResetEvent
void Reset()
Reset the event, preparing it for reuse
Task WaitAsync()
Get awaitable task for the event
void Set()
Set the event, unblocking any code awaiting the event
Defines the common methods required by all editable CSLA collection objects.
Maintains metadata about a method or property.
string Name
Gets the member name value.
Maintains metadata about a property.
Defines the common properties required objects that track their own status.
Implement if a class requires access to the CSLA ApplicationContext type.
ApplicationContext ApplicationContext
Gets or sets the current ApplicationContext object.
Interface defining an authorization rule implementation.
Csla.Core.IMemberInfo Element
Gets the element (property/method) to which this rule is associated.
AuthorizationActions Action
Gets the authorization action this rule will enforce.
Interface defining a business/validation rule implementation.
Public interfacefor IBusinessRules
Defines the interaction between the rules engine and a business object that hosts the rules.
void RuleStart(Csla.Core.IPropertyInfo property)
Indicates that a rule has started processing.
void AllRulesComplete()
Indicates that all rules have finished processing.
Interface defining callback methods used by the SerializationFormatterFactory.GetFormatter().
void Deserialized()
Method called on an object after deserialization is complete.
StateMode
Indicates the reason the MobileFormatter functionality has been invoked.
RunModes
Flags enum to define when rule is allowed or denied to run
AuthorizationActions
Authorization actions.
RuleContextModes
RuleContext mode flags
RelationshipTypes
List of valid relationship types between a parent object and another object through a managed propert...
@ Serializable
Prevents updating or inserting until the transaction is complete.