In midPoint there are many kinds of links among objects. They are differentiated by relation attribute of the particular object reference. Relations can be standard or custom. (More information on this topic can be found e.g. on Relation and Relation Configuration pages.)
These are typical linkssituations when two objects are linked:
|Situation||Link source (assignee, assignment holder)||Link target (assigned object, assignment target)||Relation||Note|
|User A has a role R.||User A||Role R||org:default|
|User A is a member of org O.||User A||Org O||org:default|
|User A is a manager of org O.||User A||Org O||org:manager|
|User A is an owner of token T.||User A||Service T (archetyped as token)||org:owner||(Or maybe org:default is more appropriate, because "owning a token" is the default relation.)|
|User A is a child of user B.||User A||User B||custom:child||This is not supported now. The only user-user assignments we support are delegation ones.|
|Role R has a metarole M.||Role R||Role M||org:default||(Or org:meta.)|
|User A is an approver for role R.||User A||Role R||org:approver|
|Yes||Yes||The usual case. There is an assignment from A to B. The assignment is valid and effective, so the information is in roleMembershipRef as well.|
|Yes||No||There is an assignment from A to B. But it is either invalid (from the activation point of view) or not effective (from the conditions point of view), so there's no record in roleMembershipRef.|
|No||Yes||There is no direct assignment from A to B, but the link was created indirectly, e.g. via some inducement. E.g. , for example from A to B, where B induces C. So A → C is not prescribed but actual.|
|No||No||A and B have no link altogether, or there is an indirect assignment that is not valid or not effective.|
When executing an operation on a link participant (focus object), one often needs to obtain a piece of data from the other link participant. Typical Basic cases are:
|flow||Mechanism to be used|
|Example (from unrelated scenario)|
A role held by a user influences the user's cost center (by copying from role's cost center).
|(role → user)||https://github.com/Evolveum/midpoint/blob/fa13ce0dd8b460fb98b049ac6b405cbe7e39957c/model/model-intest/src/test/resources/linked/archetype-token.xml#L27-L47 (this passes token name to holder's organizationalUnit property)|
|2||User's photo should be copied to the badge (service) held by the user|
assignee → assigned (user → service)
|https://github.com/Evolveum/midpoint/blob/fa13ce0dd8b460fb98b049ac6b405cbe7e39957c/model/model-intest/src/test/resources/linked/archetype-token.xml#L51-L70 (this looks for user's name+fullName when computing token's description property)|
Derived (more complex) cases are:
|#||Situation||Data flow||Mechanism to be used||Note|
|1a||A role held by a user influences "location" attribute of the user's account in a target system (by copying from role's locality attribute).||assigned|
|→ assignee projection (role → user → user account)||assignmentPath in focus mapping brings information from|
|role to user; then standard outbound mapping brings information from user to user's account.||Sometimes we can maybe use assignmentPath directly in outbound (when in construction).|
|name should be copied to|
|LDAP attribute for a token (service)|
|owned by the user||assignee|
|→ assigned → assigned's projection (user → service → service account)|
|in mapping for the LDAP attribute.|
Therefore, in order to navigate from assigned to assignee (i.e. finding assignee when recomputing assigned in scenarios 2/2a) we need to provide
midpoint.findAssignees methods. It is questionable if we also need a method to find assigned from assignee - normally this can be determined from
|Method||Meaning||Repo search needed||Example of search Search criteria||Current implementation|
|midpoint.findAssignee||Returns (at most one) assignee matching specified criteria for given assigned object.||Yes||object type||https://github.com/Evolveum/midpoint/blob/8190d0d902041dd73d3c07d2f80e2ec854e36f33/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java#L2022-L2037|
|midpoint.findAssignees||Returns all assignees matching specified criteria for given assigned object.||Yes||object type||(the same)|
|midpoint.findAssignedObject||Returns (at most one) assigned object matching specified criteria for given assignee.||No||object type = ServiceType, archetype = badge||Not implemented yet. Maybe not needed anyway.|
|midpoint.findAssignedObjects||Returns all assigned object matching specified criteria for given assignee.||No||object type = RoleType, archetype = primary business role||Not implemented yet. Maybe not needed anyway.|
Cascading the changes
We need a mechanism that will propagate changes from one side of the link to the other one: either from assignee to assigned object, or from assigned object to the assignee.
In situations #1 and #2 1/1a we need to recompute users that hold a given role if:
- relevant parts of the role
- (i.e. cost center for scenario 1 or locality for scenario 1a) changes,
- or if the user-role link existence status changes - this is usually driven by changes on the user side (assignment existence, assignment activation) but the role can influence this as well (role deletion, role deactivation - either explicit or implicit, e.g. because of time validity).
In situations 2/2a we need to recompute services (badge, token) that are held by given user when if:
- relevant parts of the user changes
- (user's photo, user's name),
- or if the
- user-service link existence status changes - this is usually driven by changes on the user side (assignment existence, assignment activation) but the service can influence this as well.
Note that #11/#2 1a are exactly what the original "recompute affected" option did. However, the option has to be set manually. Today, we have much better options.
TODO update this The most natural way how to cascade the changes is to use policy rules.
Each rule has two main parts: condition and action:
- The condition specifies when the rule will be applied. In our case, this means "something of interest has changed". So, for example, in situation #1 the condition covers modification of costCenter property. (The role deactivation/deletion will be treated later.) In situation #4 the condition covers modification of user's name. (Again, user creation/deletion, and token assignment/unassignment is treated later.)
- The action specifies what to do when the rule is applied. In situation #1 the action is "recompute assignees". In situation #4 the action is "recompute (relevant) assigned objects".
TODO update this
- What if role is deleted? We can create a policy rule that will trigger on role deletion. But in the moment of executing the scripting action the role object is already gone. (See Projector and clockwork internals.) So the solution should be to delete roles gracefully: for example by changing their lifecycle state to retired. (A similar situation is in the Grouper asynchronous integration scenario, see here.) A special care should be taken to searching for members of retired roles; they do not have role reference in their
roleMembershipRefitem. Fortunately, when such reference is missing it means that the inactivation of role's cost center already took place.
- What if role is added? Actually, nothing wrong normally occurs. If a new role is being added, it should have no assignees (except for the case of role deletion and re-creation but this can be ignored).
- When invoking policy action after token unassignment or user deletion, the token information can be already gone. Fortunately, if the token was assigned directly, we can find its traces in evaluatedAssignmentTriple. But it should be elaborated and tested well.
- User creation and deletion, and token assignment and unassignment should be treated as separate policy conditions in addition to user name change. When specifying "token assignment/unassignment" condition, one should specify what "token" is. And the same specification must be part of the "recompute assigned objects" policy action. So we need some reusable/connected specification; at least in the future.
Enabling/disabling the change propagation