Automatic reassignment - contract guarantor
@since 15.18.0
Certain identities are required to always have guarantors on each of their contracts. This feature ensures that this is maintained. When the last active guarantor of a contract is deactivated or deleted, a new guarantor (or several guarantors) resolved by a configurable script is assigned to the contract. The deactivated guarantor is then removed as a guarantor of that contract, i.e. it WILL NOT automatically be a guarantor of that contract again if reactivated. If the contract has other inactive guarantors at that moment, they will stay untouched.
Which contracts does this apply to
There is a configuration item idm.sec.core.contract-guarantee.guardedFormProjections that contains a comma-separated list of form projection codes (or identifiers) that determine which identities need their contracts to be guaranteed by someone. The feature then guards all contracts that belong to those identities except in the following cases:
- the feature is switched off (idm.sec.core.contract-guarantee.enabled=false, the default),
- the contract has already ended (validTill in the past). Nobody is responsible for a contract that is over, and an installation with a few years of history is full of them. Contract state does not disqualify it: an excluded or blocked contract still gets a guarantor, as long as it doesn't have an expired validTill.
- the contract is controlled by contract slices. Assigning a guarantee that is not in the slice's guarantee set would be deleted on the next recalculation, so the contract is reported as blocked instead and the guarantor has to be added to the slice by hand.
- the contract or its identity is being force deleted (flagged with the DELETED entity state).
How does the feature find managers and subordinates
The feature uses the configured managersFor filter set in the configuration item idm.sec.core.filter.IdmIdentity.managersFor.impl. For the correct functioning of this feature, as well as the correct functioning of IdM in general, it is necessary to make sure that the subordinatesFor (idm.sec.core.filter.IdmIdentityContract.subordinatesFor.impl) filter is the appropriate counterpart, i.e. if the managers filter ignores tree structures, the subordinates filter should ignore them as well and vice versa.
When does the reassignment happen
Reassignment is triggered when the guarantor identity is deleted or deactivated, either manually or due to contract expiration/deactivation/deletion. It does NOT happen when the IdmContractGuarantee object itself is deleted or changed - that can only happen during manual removal of the guarantor done deliberately by an administrator.
Asynchronous processing
The triggering processors are synchronous: they run when the guarantor's deletion or deactivation is about to be committed, and all they do is take a snapshot of everything the reassignment will need and publish a ContractGuaranteeFallbackEvent. The reassignment itself is done by IdentityGuaranteeFallbackResolveProcessor, which processes that event asynchronously - a guarantor of many guarded contracts would otherwise make the administrator wait for the whole reassignment. Because everything needed is carried by the event, it does not matter that the guarantor and its contracts are already gone by the time it runs.
How are the new guarantors resolved
To resolve new guarantors, there is a system script under the configuration item idm.sec.core.contract-guarantee.fallbackScript. This script takes the following input arguments:
| Name | Type | Meaning |
|---|---|---|
| identity | IdmIdentityDto | the guarded user whose contract lost its guarantor, always filled |
| guarantee | IdmIdentityDto | the departing guarantor, null when the script is run from the backfill task |
| contract | IdmIdentityContractDto | the contract that is now orphaned, always filled |
| guaranteeContracts | List<IdmIdentityContractDto> | all contracts of the departing guarantor, never null, an empty list if run from the backfill task |
| guaranteeManagers | List<IdmIdentityDto> | all managers of the departing guarantor, never null, an empty list if run from the backfill task |
Resolving guaranteeContracts and guaranteeManagers outside the script is necessary, since the script's execution is asynchronous - on guarantor deletion, it could run after the guarantor's identity no longer exists and its past managers can't be resolved. The field guaranteeManagers is only passed to the script on guarantor deletion, on guarantor deactivation the script will see them as empty lists and can resolve them itself - this allows a script that doesn't need those parameters to avoid the expensive querying. The field guaranteeContracts is much cheaper so it is filled always.
If the script returns an empty list - or no script is configured at all, which is the default - the guarantors are resolved further down a chain that always ends with somebody:
- the active holders of the role in idm.sec.core.contract-guarantee.fallbackGuarantorRole - and when that item is empty (the default) or names a role that does not exist, the admin role (idm.sec.core.role.admin, superAdminRole by default) is used in its place,
- if that role has no usable holder: the single admin identity.
Product provided scripts
| Code | Function | Parameters (in the script body) |
|---|---|---|
| contractGuaranteeFallbackTargets | Finds managers of the departing guarantor. Filters out those mentioned on the blacklist by their node or by their username | BLACKLIST_USERNAMES - list of usernames of the users that will never be assigned as guarantors BLACKLIST_NODES - list of codes of the tree nodes whose users will never be assigned as guarantors - NOT recursive |
| contractGuaranteeFallbackByUsername (demo) | Finds guarantees by a list of usernames | USERNAMES - list of names of the users to be assigned as guarantors BLACKLIST_NODES - list of codes of the tree nodes whose users will never be assigned as guarantors - NOT recursive |
| contractGuaranteeFallbackByRole (demo) | Finds guarantees by a list of roles | ROLE_CODES - list of role codes the users of which will be assigned as guarantors BLACKLIST_USERNAMES - list of usernames of the users that will never be assigned as guarantors BLACKLIST_NODES - list of codes of the tree nodes whose users will never be assigned as guarantors - NOT recursive |
The parameters live in the body of the script, not in the global configuration - so a copy of the script is configured entirely by editing its own body.
Backfill task
To assign guarantors to guarded contracts that have been left without a guarantor in the past or that have never been assigned a guarantor yet, there is a long running task "Assign fallback guarantor to guarded contracts" (core-contract-guarantee-fallback-long-running-task). This task checks all contracts of all guarded identities and uses the configured script to assign them guarantors. Note that during this operation, there is no departing guarantor, so the fields guaranteeContracts and guaranteeManagers will both be empty and the field guarantee will be null.
Trying it out first
The task supports the scheduler's standard dry run. It assigns nothing and lists exactly the contracts a real run would repair, each as a NOT_EXECUTED item - so the size of the problem is visible before anything is changed.
Configuration
The feature is off by default and nothing it needs has a usable default value, so it takes a few deliberate steps before it does anything. This is the whole procedure for the common case - the feature switched on, with the shipped manager script doing the resolving.
Step by step
- Mark the identities whose contracts are guarded.
Put the codes (or identifiers) of their form projections in idm.sec.core.contract-guarantee.guardedFormProjections, comma separated. No projection means nothing is guarded, so nothing happens.
2. Make your own copy of the script.
Go to Settings -> Scripts, open contractGuaranteeFallbackTargets, and save it under a code of your own (e.g. myContractGuaranteeFallbackTargets).
3. Fill in the blacklists in your copy.
At the top of the body there are two lists:
List BLACKLIST_NODES = [];
The script resolves the managers of the departing guarantor, and whoever it returns becomes a guarantor of the guarded contract. That is not always wanted: technical and service accounts, a shared "HR" identity, the managers of a department that must not take externists over. Name them in BLACKLIST_USERNAMES (usernames), or name whole organisation units in BLACKLIST_NODES (tree node codes) to exclude everybody holding a contract there.
The node blacklist is not recursive - the main usecase is preventing the guarantorship propagating too high through the company structure, so blacklisting a node only blacklists the node itself, not the nodes below it, so list every node that has to be blocked.
4. Point the feature at your copy.
idm.sec.core.contract-guarantee.fallbackScript=myContractGuaranteeFallbackTargets
5. Configure the fallback guarantor role.
idm.sec.core.contract-guarantee.fallbackGuarantorRole names the role whose active holders take the contract over when the script resolves nobody - everybody it found was inactive or blacklisted, or the departing guarantor had no manager at all. Optional, but see the warning in step 6: leave it empty and the admin role is used in its place, with the admin identity behind it if nobody holds that either.
6. Switch the feature on.
idm.sec.core.contract-guarantee.enabled=true
From this moment the guarded contracts are kept guarded - but only from this moment. Contracts that lost their guarantor earlier are repaired by the backfill task, Assign fallback guarantor to guarded contracts, which you run by hand from Settings -> Scheduled tasks. Use Dry run first: it changes nothing and lists exactly the contracts a real run would repair.
7. Schedule the backfill task, if you need it as a safety net.
The product does not schedule it. Schedule it yourself at the end of your chain of HR processes, whose deactivations are what leaves a guarded contract unguarded.
Configuration values
idm.sec.core.contract-guarantee.guardedFormProjections=identity-guarded,identity-guarded2
#the code of the role by which the new guarantors are resolved if the script returns an empty list
idm.sec.core.contract-guarantee.fallbackGuarantorRole=
#the code of the script that resolves new guarantors
idm.sec.core.contract-guarantee.fallbackScript=
#on-off switch for this feature
idm.sec.core.contract-guarantee.enabled=false
#filter to find managers for a subordinate
idm.sec.core.filter.IdmIdentity.managersFor.impl=
#filter to find subordinates for a manager
idm.sec.core.filter.IdmIdentityContract.subordinatesFor.impl=
All four contract-guarantee properties are exposed on the contract-guarantee configuration agenda.
Notification
There is a notification sent to the users that have been assigned new guarantors to a guarded contract. The new
notification configuration created by the init processor is as follows:
| Item | Value |
|---|---|
| Topic | core:contractGuaranteeFallbackAssigned (CoreModule#TOPIC_CONTRACT_GUARANTEE_FALLBACK_ASSIGNED) |
| Template | contractGuaranteeFallbackAssigned (IdmCoreContractGuaranteeFallbackAssigned.xml) |
| Recipient | Each newly assigned guarantor (one per message) |
| Parameters | identity (the recipient), guardedIdentities, contracts, originalGuarantee, reason |
The reason parameter is one of IDENTITY_DELETED, IDENTITY_DISABLED or BACKFILL.
The notification is sent as one notification per batch, i.e. if multiple guarantors are deactivated using a bulk action or an HR process and some of the reassigned contracts have the same fallback guarantor, the fallback guarantor will only receive one notification for that batch with a list of contracts assigned to them. Similarly if one guarantor of multiple contracts is deactivated, the fallback guarantor will only receive one notification listing all the assigned contracts.
Implementation notes
| Class | Usage |
|---|---|
| ContractGuaranteeConfiguration | Resolves script, guarded projections and fallback role from configuration |
| DefaultContractGuaranteeConfiguration | Reads the properties, resolves projection and role codes or identifiers through LookupService |
| ContractGuaranteeTransferManager | Contains the logic of resolving guarantors, subordinates, contracts... |
| ContractGuaranteeFallbackEvent | Carries the snapshot of script arguments, needed to preserve consistency when reassigning asynchronously on identity deletion |
| IdentityGuaranteeFallbackDeleteProcessor / IdentityGuaranteeFallbackDisableProcessor | Catch identity delete and identity disable events, emit the event for IdentityGuaranteeFallbackResolveProcessor, synchronous |
| IdentityGuaranteeFallbackResolveProcessor | Asynchronous processor, does the reassignment itself |
| ContractGuaranteeFallbackTaskExecutor | The backfill long running task (core-contract-guarantee-fallback-long-running-task) |
| IdmIdentityFilter#setManagersFor / IdmIdentityContractFilter#setSubordinatesFor | The two configurable filters the feature asks from both ends - see How does the feature find managers and subordinates |
| ContractGuaranteeFallbackNotifyListener | Sends the aggregated notification once the transaction of the ending long running task commits |
| IdmIdentityContractService#findByIdentityFormProjection | Candidate query of the backfill task |
| contractGuaranteeFallbackTargets / …ByUsername / …ByRole | Shipped scripts |
| IdmCoreContractGuaranteeFallbackAssigned | Notification template |