Grouper rules are configurable declarative scripts which run at certain times and perform actions on the registry. They are similar to hooks though you dont have to write Java, and it does not require a change to a config file to enable a rule (i.e. anyone with authority in the folder hierarchy could enable a rule).
Composite-ng: If an entity is no longer a member of the employee group, remove them from the group for application X
Disabled-date activation: If a student is no longer a member of the course X group, then add a membership to the course wiki group with end date in one week (note, this assumes that if the student is out of the course group, they fall out of the wiki group, another variation is to set an end date on an existing membership)
Composite-org: If an entity falls out of any group in the IT organization groups (meaning not a central IT employee anymore), then remove them from group X
Inherited permissions: If a group is created under folder a:b, then apply privileges to the group of READ,UPDATE to group a:security:admins
The rule structure is custom for Grouper since we want it to be performant and secure, however it is inspired from drools. There are several parts to a rule:
- actAs: subject that the rule will act as. If blank, then it will be filled in with the user who created the rule (probably a bad idea since the person might leave at some point, unless it is a service principal). There can be configurations in the grouper.properties which allow users to act as other users or GrouperSysAdmin.
- check: this is when the rule is fired. This will generally have a checkType, which tells grouper when to fire the check, and some data which narrows down the search. e.g. checkType could be flattenedMembershipRemove, and the data could be groupName: a:b:c. the data is stored in the checkOwner attribute
- ifCondition: this might not be needed if the check contains all the information about when the rule should fire. Otherwise you could put an EL here about something to check. e.g.
- then: this is a scriptlet or maybe some built in actions. e.g. thenType is removeMember and groupName is a:b, or a scriptlet:
The check component will see if the rule should continue to the "if condition". The check part is an enum class: edu.internet2.middleware.grouper.rules.RuleCheckType
Look at the javadoc or source for the most recent check types, currently they are:
Here is an example of setting a rule check:
The second part of the check is the owner. This can either be set by name or id. If the check is for objects in a folder or subfolder, there is also a stem scope attribute for ONE or SUB
The rule will be an attribute of a grouper object (group, stem, etc). There are attributes on the assignment which configure the params
If the rule is not scripted, then we have the opportunity to run it in daemon mode at the time the rule was added or changed, or periodically (nightly/weekly) to reduce data corruptions. Some rules might not want this to happen (e.g. on group create set permissions, if you do this nightly then you cant remove permissions)
If the rule execution fails for some reason, it should be logged (which could include emailing administrators), but it probably should not affect the transaction of the operation that triggered the rule. Maybe this can be a setting on a per rule basis and where applicable (e.g. if it is a flattened membership rule trigger, then there is no transaction since the rule fires post commit anyways.
Note that the subject source should be set before the subject id or identifier (if the id or identifier arent unique). Anyways, you can act as yourself, though I dont know why you would want to do that since if you leave the institution the rule might break. You can configure in the grouper.properties what the act as rules are, similar to the grouper WS act as.
There are certain validation constraints to make a rule valid. i.e. you need some check, you need some then, you need an act as subject, etc. So each time you change a rule attribute value, all the attributes are validated, and the attribute "ruleValid" is managed by that hook. If the rule attributes are not valid, you will get a ruleValid value of something like: "INVALID: Rule check type required", if they are valid, then the value will be "T". Only rules with a value of T will be processed. The attribute stores this state so the rules dont have to be validated each time they are read from the DB, and so the user can get some feedback.
TODO: a daemon should validate rules daily, and ones which arent valid should be logged (notified)
TODO: when processing all rules, filter out ones which arent valid
You can turn debug logging on to see information about rules which fire. log4j.properties
If you want to only log certain rules, you can specify them in the grouper.properties. (and you need to set the RulesEngine to INFO level at least)
You will see log messages like this:
You can have the "then" clause veto an action (if it is a transactional check), by using the grouper util veto EL method. Note, if you are writing a custom EL class and want a veto, return the exception, dont throw it. Also the exception should be a RuleVeto exception (which is runtime) or a subclass. This example will veto an add to group A if the person is not a member of group B
There is a daemon which runs on the loader which validates the rules and marks invalid ones as invalid. Those need manual fixes to get them valid again (due to actas permissions). You can configure the quartz cron in the grouper-loader.properties:
The daemon will also run rule logic to sync up data inconsistencies (if it slipped by the rule somehow, or existed before the rule did). The rule must be eligibile for daemon logic, meaning it must have an enum for the CHECK part, or either a blank or enum IF condition. Also, the CHECK and IF enum must support daemon logic (basically it needs to be implemented), and the "ruleRunDaemon" attribute must be blank or T, and not F.
Extended EL API
There is a special group which has access to more objects in EL:
This is because the RuleUtils class might be too limiting in some cases, but if everyone had access to the API, it might not be secure. So if you need this, configure a group here, put in trusted admins/users, then act as those users in your rule. e.g.in this case the attributeAssignType object is in scope
Note, to see which objects are in EL scope, turn debug logging on for rules and check the logs
Custom EL classes
You can configure custom EL classes to help with logic you need if not in the Grouper API. Here is an example:
Make a class:
Use that in an EL:
- document examples in GSH / API / shorthand for all use cases
- add an API for easy rule assignment
- rules on notification events
- separate ruleUtils with ruleElUtils
- test the membership update, and other unit tests
- add daemon for add groups with privs
- hook to run daemon? so that when rule is established, the daemon part syncs everything up?