Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Include Page
spaceKeyGrouper
pageTitleNavigation

Overview

To implement access policies, it has often been necessary to set up intermediate groups, include/exclude, requirement groups, and allow/deny manual groups. Grouper has features to help in this area including: rules, hooks, templates, move/copy, import/export, and GSH scripts.

The ABAC with scripted groups feature is designed to offer increased efficiency in implementing access policies.  It's important for the common groups and policy language to be well documented and people to be properly trained.

JEXL loaded groups

In Grouper v2In Grouper 2.6.6+ there is a first pass at JEXL loaded groups using memberships of groups onlyIn v5+ scripted groups can also be based on entity data fieldsIt 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.

Info
titleSee the blog!

For more info, see the February 2022 blog on Attribute Based Access Control with Grouper.


Video

Expression language (JEXL) scripts facilitate implementing the part of ABAC that defines who is included in a policy based on attributes of those users.  Other parts of ABAC such as resource attributes or environment attributes can be taken into consideration with Grouper permissions or by the service which has protected resources.

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.

...

  • 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 near 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 v5+ an incremental and run the full nightlyjob will adjust the members quicker.  Note: there is one full daemon and one incremental 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

...

ExpressionDescription


Code Block
${ entity.memberOf('ref:staff') && entity.memberOf('ref:payroll:fullTime') && entity.memberOf('ref:mfaEnrolled') }


Three part intersection.  

Full time staff in MFA


Code Block
${ ( 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


Code Block
${ 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

Allow common reference groups to have user friendly labels

Code Block
e.g. employee instead of ref:employee

...

e.g. 

Code Block
FROM
Member of employee, and member of app:jiraAdminsManualTO
${entity.memberOf(‘ref:employee’) && entity.memberOf(‘app:jiraAdminsManual’)}

e.g.

Code Block
FROM
Has affiliation attribute with name of staff and dept of english, 
   but not a member of lockout

TO
entity.hasAttribute('affiliation', 'name==staff && dept==english') 
   && entity.notMemberOf('ref:lockout')

...

Entity attribute resolver groups

This is for a future release

The high level strategy is you need two database tables, a table of people and a table of one-to-many records about those people.  This will be configured in your global entity attribute resolver.  abac jexl scripted groups would be able to use those similar to this

Code Block
my_people_table
employee_id (same as subject id or identifier)  email
123                                             a@b.c
234                                             b@c.d

my_people_affiliation
employee_id    affiliation_name   dept
123            staff              english
123            student            history
234            guest              chemistry

${ entity.attributeRecordFilter('myPeople', 'affiliation', "(affiliation_name == 'staff' || affiliation_name == 'student') && dept == 'english'") }

The first argument "myPeople" identifies the entity resolver
The second argument "affiliation" identifies a one-to-many relationship on the main entity resolver table which is another table
The third argument is a filter on that one-to-many table for those people
This means find people where the affiliation has a name of staff or student and that affiliation is in dept english.   The format of the filter would not be a well defined subset of jexl...  thoughts?  Image RemovedNote: this is a little weird since there is a JEXL script embedded in the JEXL script...  but we can document it and see how it goes? 

Entity resolver change log or last updated col could be used on incremental

Liam Hoekenga  2 minutes ago
why do you need the my_people_table?  is there data already in grouper that could be used instead (GROUPER_MEMBERS)?
Chris Hyzer  < 1 minute ago
yeah, i was kinda thinking that too.  guess it could just be a table which has a col that could be mapped to a subject id or identifier...
Chris Hyzer  < 1 minute ago
the entity resolver currently has a table or subjects, one per row.  So I think the two tables makes sense.  The person table could just be a view of select distinct subject_id from second table right?  :slightly_smiling_face:
Chris Hyzer  < 1 minute ago
or with one table we would just have the entity resolver be multi valued and have one table... need to think about that :slightly_smiling_face:

Parse expression with JEXL (for Grouper developers)

...

Feed the expression through this simple program

Code Block
  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

Code Block
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

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

...

Code Block
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: primaryAffiliation is faculty or staff and dept is physics or math

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

...

in v5+

The script is parsed and converted to SQL.  The results represent the members of the group.  The diffs will be added or removed from the group.

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

...


Image Added


Policy patterns

Your institution can make a GSH template that will help users setup policies

TODO document this


See Also

Access Management Features Overview

...