In Grouper 2.6.6 there is a first pass at JEXL loaded groups. It is basic and can be built on. Note: this is subject to change as we see a working solution and discuss the optimal path forward
We want to be able to craft policies by an expression instead of creating loaders or tons of reference groups based on cartesian products of basis/ref groups.
Individual groups can be configured to automatically have their membership managed with individual subjects (or in future groups as members)
Why do we need this feature?
- Reduces pre-loaded rollups that might not be used
- You don't need a loader job for each one of these groups
- Any Grouper user could edit the policies if they can READ underlying groups. The expressions are secure (future state)
- The memberships of the ABAC groups are real time based on an intelligent change log consumer (future state)
- You can have a UI to help build it and give good error messages
- Could visualize the policies. Perhaps could be integrated into existing visualization (future state)
- This solves the issue of composites with any number of factors
UI to configure
Daemon screen
Note in Grouper v2.6.6 you need to wait an hour after changing a script, or run the JEXL script loader full job. In the future we will have an incremental and run the full nightly. Note: there is one full daemon that handles all of the JEXL script ABAC groups. You do not add this, it is built-in
Scripts
The script can only be written by people who can READ groups in the script and UPDATE the owner group. Since this is actually a JEXL script (not a JEXL expression), so you could have multiple lines, variables, conditionals, etc
In an entity script, the variable 'entity' is an instance of class: edu.internet2.middleware.grouper.abac.GrouperAbacEntity
You can use entity.memberOf('full:group:id:path') exactly like that to see if user is in a group or not.
Expression | Description |
---|---|
${ entity.memberOf('ref:staff') && entity.memberOf('ref:payroll:fullTime') && entity.memberOf('ref:mfaEnrolled') } | Three part intersection. Full time staff in MFA |
${ ( entity.memberOf('ref:employee') || entity.memberOf('ref:student') // employees or students || (entity.memberOf('ref:guests') && entity.memberOf('app:vpn:vpnManualOverrides'))) // or guests who are in manual allow && !entity.memberOf('ref:globalLockout') && !entity.memberOf('app:vpn:vpnManualLockout') } // and not in either lockout group | Example policy That means users who are not in globalLockout and not in vpnManualLockout |
${ entity.memberOf('app:vpn:users') != entity.memberOf('ref:mfaEnrolled') } | Exclusive OR This is VPN users not in MFA and MFA users not in VPN: |
How it works
There are some trade-offs with performance and resources. This is the current implementation. It is optimized to reduce run-time. It does use a lot of memory, though that was a consideration.
- Sees which groups are in the script
- Get the memberships of the owner groups and all script groups (only get the memberId, sourceId, and groupId)
- Consider if configured to include internal subject sources (adjust the membership lists)
- For each member of either the owner group or the script groups for that owner
- Setup the variables for a JEXL script based on the bulk queries
- Evaluate the script
- If the result does not match the current state of the membership, add or remove the member from the owner group
- If a script evaluation fails, proceed with the job
TO DO
- Load groups as members
- Add incremental job
- Unit tests
- Better validation
- More methods to call (other than hasMember)
- Add subject attributes e.g. from global attribute resolver
- Failsafes
- Add delegated admin based on READ of groups
- Visualization
- Add more counts of total memberships etc
- See if comments can be included in scripts
- See if we can replace composite type with immediate in membership table if replacing a composite with jexl script
Parse expression with JEXL (for Grouper developers)
Analyze policy
To confirm a policy is correct, a long form translation of the policy can be displayed along with group names and group counts (future state)
Visualization
This is a complicated topic since it is parsing a programming language.
As a first pass we could have the overall group and lines from all component groups with count and the exact policy isnt there. This would apply for any unparsable policies once we have better visualization.
In a future pass, if the script follows certain standards and is "parsable" (allow-deny where parens and multiples could be involved), then I could picture a visualization for that. Note: either the visualization will be slow, or lots of data will be cached, or it will be stale from the last full sync. This is because JEXLs cannot be transformed into queries with counts, it needs the data in memory to allow JEXL to do its thing.
We need to get a list of sample policies people want to use so we can make sure we are going in the right direction.
Full sync
A nightly full sync will occur. The incremental sync should stop. Make sure all the loaded groups are up to date.
Incremental sync (future state)
An incremental change log consumer can
- If group attributes change, see if it affects group attributes (future state)
- If attributes change, see which policies those refer to, and incrementally adjust the membership of those groups
- Policy changes should change the population