Gruppetrær

Forslag til gjenbrukbar løsning for oppretting og vedlikehold av automatiske grupper og gruppehierarkier i Cerebrum.

Selve utkastet til design ble opprinnelig påbegynt som notater og kode-dokumentasjon, og er derfor skrevet på engelsk.

Bakgrunn

I Prosjekt Tilgangsstyringsgrupper (TSG) er det foreslått flere kategorier med automatiske grupper — de fleste organisert i en eller annen form for hierarki.

Det finnes allerede en del slike gruppehierarkier i Cerebrum, men ingen felles funksjonalitet for gruppehierarkier eller -trær. I forbindelse med implementasjon av den første kategorien med automatiske grupper fra TSG, ORG-ERA-grupper, vil vi forsøke å generalisere og samle slik funksjonalitet.

Løsningen etterstreber å løse behov for de nye ORG-ERA-gruppene, samt være generell nok til å fungere som basis for eksisterende automatiske grupper. Ved å se på flere, ulike use-cases, vil vi forhåpentligvis ende opp med en løsning som er fleksibel nok til å også håndtere andre, fremtidige behov for automatisk vedlikehold av grupper.

Group trees

Group trees are collections of groups where:

  1. All the groups are of a given group type (Group.group_type)

    All groups of this group type are considered members of a given tree. No other groups should be assigned the given group_type — the group type is reserved for a given group structure.

  2. Any one group in the collection can only be a member of one parent group of the same type.

    This ensures that the groups form an actual tree structure. However, groups in the collection can still be members of groups with a different group_type (i.e. groups not included in the tree structure).

    Notat

    This is an important constraint if the group tree ever needs to be re-organized. We could omit this for group trees where the structure itself is maintained through a separate sync (full sync).

  3. All group memberships within the structure are automatically maintained.

    Groups outside of the group structure (other group_types) cannot be members of groups within the structure.

  4. Each group only contains members of a given type (either groups or other objects).

    • Groups that contain other groups are only there to represent parent-child relationships within the tree structure.
    • Groups that contain other objects should only be placed as leaf nodes in the tree.

Maintaining the tree structure

A typical use case for a group tree is to represent a hierarchy of roles. Building such a tree can be split into three problems: Translating roles into groups, Building a tree structure from a set of groups, and Updating the tree.

Translating roles

The first problem involves translating roles into groups.

If we assume that all the roles we encounter can mapped to a leaf node in an imaginary tree, then what we need is to fetch all nodes from the leaf node to the root node. Then we treat each node in this imaginary tree as a group, and each parent relationship as a group membership.

The path of nodes from the leaf node to the root node forms an actual sub-tree in our group tree.

Example: Given the role foo, which is a subset of bar, which again is a subset of baz, we'd get a list of groups, [role-foo-bar-baz, role-foo-bar, role-foo].

Different tree structures and role sources would implement their own translators:

Affiliation groups
An employee group tree, where the affiliation ANSATT@352100 is translated into [ansatt-352100, meta-ansatt-352100, meta-ansatt-352000, ..., meta-ansatt-900000]
ORG-ERA groups
An employee role with (OU=matnat, SKO=foo YRK=123) is translated into: [era-matnat-foo-123, era-matnat-foo, org-matnat, employees]
Manager groups
An employee with the manager role at OU=usit is translated into: [managers-at-usit-los-uio, managers-at-los-uio, managers-at-uio]

The lists of groups represents the path from leaf to root in a given group tree.

Building a tree

Whenever a leaf group from a path needs to be updated, we start off by asserting that the path exists as expected within the tree:

  1. Ensure that each of the groups exists:
    • If the group already exists...
      • with an expected group type: ensure up to date (description, etc...)
      • with an unexpected group type: critical error - we don't want hijack groups from other group trees or groups used for other purposes.
    • If the group doesn't exist: create a new group with the appropriate name, type, description, etc..
  2. Ensure that each group is only a member of its parent group:
    • Note that the groups can be members of other group types - but they must be limited to one membership within the group tree.
    • If not member of any groups: Add membership (except for the root group)
    • If member of another group within the tree: Structural change, move membership to the new group
    • If member of multiple groups within the tree: TODO: as with one other group, i.e. remove existing memberships, add membership to new parent? or is this some critical error?

TODO/TBD

What about simple removals? If we only observe individual members and their leaf node memberships, we can never really maintain/clean obsolete groups. They will empty out whenever a given role disappears/all its members disappear.

If nobody has a given role, maybe the role should cease to exist? Could we simply delete empty groups? Permanently?

TODO/TBD

Step #2 can only work if we can trust our data source and/or tree translation. If one person is continually assigned [foo-bar, foo], and another is assigned [foo-bar, baz], the foo-bar group will keep getting re-assigned as a member of either foo or baz.

Updating the tree

The most common scenario for updating a group tree is to sync all roles assigned to an object with its appropriate leaf groups.

To do this, we would:

  1. Translate roles into paths (see Translating roles)
  2. Ensure the tree structure itself is in sync with each path (see Building a tree)
  3. Reduce the paths into a set of leaf groups (the first group from each path).
  4. Ensure that the object is not a member of any groups within the group tree that is not in the set of leaf groups.
  5. Ensure that the object is a member of each group in the set of leaf groups.

Notat

The current memberships within a group tree can e.g. be fetched with Group.search_members(group_type=<group_types>, member_id=<member-id>, member_filter_expired=False). We only need to consider current, direct memberships.

The inverse (i.e. sync all memberships for a given role) would be very similar, as long as the required information is available:

  1. Translate role into path
  2. Ensure the tree structure itself is in sync with the path
  3. Ensure that the leaf group is up to date with a given set of members.

Complex trees

When building or updating a group tree, we need a complete picture of all members of a given group within the tree, or all memberships of a given member within the tree.

This presents a problem when e.g. building a group tree from multiple sources, e.g. both employee and student data. We will typically only see a partial picture at a given point in time. If we deconstruct this group tree into two, separate subtrees, we could allow each subtree to be updated separately without affecting the other.

This would solve our issue for both current use-cases that would require role-data from separate sources:

  • ORG-ERA: Allows for separate trees, as all nodes are attached to the path (root, employees) or (root, students).
  • Affiliation groups: Are currently organized into two separate trees (meta-student-<root-ou>, meta-ansatt-<root-ou>).

We may want to add some support for merging subtrees properly. I.e. allow both group type foo and bar as parents in a given tree, but only create new groups of type foo, and only look at foo when inspecting members of the leaf node groups.

Implementation

The implementation of group trees in Cerebrum should be split into multiple parts:

Generic tree builder

A module, class, function, or collection of these that implements common functionality for e.g.:

  • Creating groups from a list of groups (path)
  • Finding current leaf group memberships for a given entity
  • Update memberships for a given entity, given a set of leaf groups
Translators
Takes a specific set of input (e.g. affiliation, org-unit) and generate a suitable list of groups. Each translator would probably be very specific for a given tree and data source.
Abstract group tree
Groups common functionality for building group trees.
Group tree implementation

Implementation of an 'abstract group tree'.

This implementation would typically define and constrain a group tree to a specific translator, and a specific group type.

Av fhl
Publisert 13. nov. 2020 17:22