Scripts allow you to change the behavior of the application without restarting the server. Another reason is that they allow only some classes (services, managers, etc.) in scripts, see script authorization. A script is also able to call another script from within itself. Also, scripts can be called by an evaluator, see section scriptEvaluator.
A frontend agenda was created to simplify the use of scripts. The agenda allows basic CRUD operations (create, read, update, delete). The basic definition of a script requires a unique code and not unique name and category. As an optional attribute there is a description. We strongly recommend that you put some user description of the script in the description attribute. On creating a new script, it's allowed to add a script authorization section with authorization. The body of a script is in the Groovy language. You can find more about Groovy here.
While a script is executed, each used class in the script body is checked. Every class must be allowed in the allowed classes or services. The check is made by the GroovySandboxFilter in the method filter. In this method is resolved class wrapped as proxy to simple classes. This control prevents undesirable use of classes.
It's possible to add authorities for the script after it's created. The authorities consist of classes and services, which can be used in the script. These authorities are only for the script that owns these authorities. Scripts don't share authorities between each other.
Permissions for each script are of two basic types:
The following classes don't need to be specially added as script authorities (they are allowed by default) - GroovySandboxFilter
String.class, Integer.class, Double.class, Long.class, Date.class, Enum.class, Boolean.class, BigDecimal.class, UUID.class, Character.class, GuardedString.class, DateTimeFormatter.class, DateTimeFormat.class, DateTime.class, String[].class, LocalDateTime.class, Map.class, HashMap.class, List.class, ArrayList.class, Set.class, HashSet.class, LoggerFactory.class, Logger.class, ch.qos.logback.classic.Logger.class
Scripts call other scripts via scriptEvaluator (scriptEvaluator is automatically added into its parent script - for example transformation to, and with builder is sent to child scripts). The instance of scriptEvaluator must be named as scriptEvaluator.
Example of calling another script with code 'test':
def result = scriptEvaluator.evaluate( scriptEvaluator.newBuilder() .setScriptCode('test') .addParameter('scriptEvaluator', scriptEvaluator) .addParameter('text', 'this is example text') .build());
We've called the test script. The script has as parameters scriptEvaluator (script can call another script - CAUTION though, scripts can call only scripts from the same category, or from a BASIC category). The next parameter is called text, the attribute text contains some sample text. The result from the evaluated script with code test will be in the result attribute. The parent script must have authorities for classes that the child scripts returns.
Caution: authorities used in the script aren't shared with the child or parent script.
Currently, there is no control loop script between the pair.
// a sample loop script in between the pair, script A (code: parent) and script B (code: child) // script A (code: parent) scriptEvaluator.evaluate( scriptEvaluator.newBuilder() .setScriptCode('child') .build()); // script B (code: child) scriptEvaluator.evaluate( scriptEvaluator.newBuilder() .setScriptCode('parent') .build()); // script A calls script B - an infinity loop
If you use classes that are not among the allowed authorities, an exception IdmSecurityException is thrown. The exception is caught in AbstractScriptEvaluator. The exception contains code, name and category of the script that has thrown the exception. The logged exception is currently only for scripts evaluated by means of a scriptEvaluator.
A script inside another script can be called with the scriptEvaluator instance. scriptEvaluator is the child of class AbstractScriptEvaluator. Children are classified by ScriptCategoryEnum. It is only permitted to call scripts within the same category or basic category.
As a parameter metody evaluate in scriptEvaluator you add Builder. The only required parameter is the code of the script.
scriptEvaluator.evaluate( scriptEvaluator.newBuilder() .setScriptCode('SCRIPT_CODE_001') .addParameter('scriptEvaluator', scriptEvaluator) .addParameter('attributeValue', attributeValue) .addParameter('entity', entity) .addParameter('text', 'test') .build());
DefaultTransformToResourceEvaluator - evaluator for transformation to a system, can call a script from the TRANSFORM_TO or DEFAULT categories. The evaluator is automatically added into the transformation to a system.
DefaultTransformFromResourceEvaluator - evaluator for transformation from a system, can call a script from the TRANSFORM_FROM or DEFAULT categories. The evaluator is automatically added into the transformation from a system.
DefaultScriptEvaluator - Evaluator for the DEFAULT category. The default script can be called from other scripts.
Code for script calling can be generated by a frontend component script area (see the first picture and button insert script). Modal window (second picture) is a list of all available scripts.
For each script, a logger is enabled. Example of some messages:
org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger("script_name"); LOG.info("INFO Log"); LOG.error("ERROR Log"); LOG.warn("WARN Log");
If you want to see debug messages, you must initialize a LOG with prefix eu.bcvsolutions.idm.. Or update logback-spring.xml.
org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger("eu.bcvsolutions.idm.script_name"); LOG.debug("DEBUG Log");
To print a message to log, you can use the classic System.out.println statement, or better print:
println("test")
The second one doesn't need additional authorities.
Scripts agenda provides a backup of all scripts that are loaded in the CzechIdM. First, you must define a path to store the backup files. The path is defined by the application property: idm.sec.core.backups.default.folder.path. The backup folder must have permissions for read+write+execute, for the user that runs the application server (e.g. tomcat).
Every script will be saved into this folder with this name: <scriptName>_<actualCzechIdMUser>_<timestamp>.xml
The script agenda also provides functionality for the redeploy of existing scripts. Only the existing new script is deployed to IdM on application restart.
For redeploy you can use backup files. CzechIdM loads all files in a defined folder with a defined suffix (idm.sec.core.script.folder and idm.sec.core.script.fileSuffix). The file name does not matter. Script is loaded by code defined inside xml.
Redeploy folder is defined by the property: idm.sec.core.script.folder. The property can contain the class path or folder. A sample folder: file:///opt/czechidm/backup/
, a sample class path:
classpath*:/eu/bcvsolutions/idm/scripts/
If you use a file folder, CzechIdM must have read+write+execute permissions on the folder that contains scripts as well as read permission on files.