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?


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.

ExpressionDescription


${ 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
and in an eligible population which is faculty, students, or guests who are in the manual app override group


${ 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.

TO DO

Parse expression with JEXL (for Grouper developers)

Feed the expression through this simple program

  public static void main(String[] args) {
    
    JexlEngine jexlEngine = new JexlEngine();

    ExpressionImpl expression = (ExpressionImpl)jexlEngine.createExpression("group.campus !~ ['palmer', 'southern'] and group.termStart - 7 > sysdate");
    
    ASTJexlScript astJexlScript = (ASTJexlScript)GrouperUtil.fieldValue(expression, "script");
    printNode(astJexlScript, "");
    
    System.out.println(expression);
  }

  public static void printNode(JexlNode jexlNode, String prefix) {
    System.out.println(prefix + jexlNode.getClass().getSimpleName() + (StringUtils.isBlank(jexlNode.image) ? "" : (": " + jexlNode.image)));
    String newPrefix = StringUtils.isBlank(prefix) ? "- " : ("  " + prefix);
    for (int i=0;i<jexlNode.jjtGetNumChildren();i++) {
      printNode(jexlNode.jjtGetChild(i), newPrefix);
    }
  }

Output

ASTJexlScript
- ASTAndNode
  - ASTNRNode
    - ASTReference
      - ASTIdentifier: group
      - ASTIdentifier: campus
    - ASTReference
      - ASTArrayLiteral
        - ASTReference
          - ASTStringLiteral: palmer
        - ASTReference
          - ASTStringLiteral: southern
  - ASTGTNode
    - ASTAdditiveNode
      - ASTReference
        - ASTIdentifier: group
        - ASTIdentifier: termStart
      - ASTAdditiveOperator: -
      - ASTNumberLiteral: 7
    - ASTReference
      - ASTIdentifier: sysdate

Grouper can take that object model and see which group and subject attributes are related, print out a nice analysis of the policy, and know which policies are affected by real time changes

Expression 2: campus is palmer or southern, or the term is current with some overlap

group.campus =~ ['palmer', 'southern'] or (group.termStart - 7 > sysdate and group.termStart - 7 < sysdate)



ASTJexlScript
- ASTOrNode
  - ASTERNode
    - ASTReference
      - ASTIdentifier: group
      - ASTIdentifier: campus
    - ASTReference
      - ASTArrayLiteral
        - ASTReference
          - ASTStringLiteral: palmer
        - ASTReference
          - ASTStringLiteral: southern
  - ASTReference
    - ASTReferenceExpression
      - ASTAndNode
        - ASTGTNode
          - ASTAdditiveNode
            - ASTReference
              - ASTIdentifier: group
              - ASTIdentifier: termStart
            - ASTAdditiveOperator: -
            - ASTNumberLiteral: 7
          - ASTReference
            - ASTIdentifier: sysdate
        - ASTLTNode
          - ASTAdditiveNode
            - ASTReference
              - ASTIdentifier: group
              - ASTIdentifier: termStart
            - ASTAdditiveOperator: -
            - ASTNumberLiteral: 7
          - ASTReference
            - ASTIdentifier: sysdate


Expression 3: campus is palmer or southern, or the term is current with some overlap

person.primaryAffiliation =~ ['faculty', 'staff'] and person.dept =~ ['physics', 'math']



ASTJexlScript
- ASTAndNode
  - ASTERNode
    - ASTReference
      - ASTIdentifier: person
      - ASTIdentifier: primaryAffiliation
    - ASTReference
      - ASTArrayLiteral
        - ASTReference
          - ASTStringLiteral: faculty
        - ASTReference
          - ASTStringLiteral: staff
  - ASTERNode
    - ASTReference
      - ASTIdentifier: person
      - ASTIdentifier: dept
    - ASTReference
      - ASTArrayLiteral
        - ASTReference
          - ASTStringLiteral: physics
        - ASTReference
          - ASTStringLiteral: math





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)

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