9.2:documentation:transformation_scripts:dev:scripts

Groovy scripts and their authorization

Scripts allows change behavior of application without restart server. Next reason is allow only some classes (services, managers and etc.) in scripts see script authorization. Script is also able call another script from itself. Another script is called by evaluator see section sciripEvaluator.

To simplify working with script was created frontend agenda. The agenda allow basic CRUD operation (create, read, update, delete). Basic definition of script reguireds unique code and not unigue name and category. As not mandatory attribute is there description. In description is recommended put some user descibe of this script. After createing script is allowed add script authorization seesection with authorization. Body of script is in groovy more about groovy you can found there.

While execute script it was dynamicaly checked each used classes in script body. For each this class is checked if exists in allowed classes or services. The control is made by GroovySandboxFilter method V průběhu evaluování skriptu jsou dynamicky kontrolovány jednotlivé třídy v tělě skriptu a je ověřována jejich existence v povolených třídách. O tuto kontrolu se stará GroovySandboxFilter, přesněji metoda filter. In this method is resolved class wrapped as proxy to simple classes. This control prevents of unwanted use classes.

After script creation is possible add authorization for this script. This is classes and services whith can be used in the sript. These authorities is only for the script that own these authrotites. Scripts doens't share authorities between each other..

Permissions for each script has two basic types:

Allowed classes

This is list allowed classes, these classes doesn't needed special authority - 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 another script via scriptEvaluator (scriptEvaluator is automaticaly added into parent script - for example transformation to, and with builder is send to child scripts). Instance of scriptEvaluator must be named as scriptEvaluator.

Example of call another script with code 'test':

def result = scriptEvaluator.evaluate(
    scriptEvaluator.newBuilder()
        .setScriptCode('test')
        .addParameter('scriptEvaluator', scriptEvaluator)
        .addParameter('text', 'this is example text')
	.build());

We called script test. The script has as parameters scriptEvaluator (script can call another script - BEWARE script can call another script only from same category, or from BASIC category). Next parameter is called text, the attribute text contains some example text. Result from evalueted script with code test will be in attribute result. Parent script must has authorities for class that child script return.

Beware, authorities used in script isn't shared with child or parent script.

Currently isn't control loop script between each others.

// example loop script between each others, script A (code: parent) a 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 call script B - infinity loop
Beware if at the end doesn't exist return statement, script return last initialized attribute.

If we used another classes that not in authorities it will be throws exception IdmSecurityException, the exception is catched in AbstractScriptEvaluator, the exception contains code, name and category of script that exception throws. The logged exception is currently only for script that is evaluated via scriptEvalutor.

Script inside another script can be called with instance scriptEvaluator. scriptEvaluator is child of class AbstractScriptEvaluator. Children is classified by ScriptCategoryEnum. Ist allowed call only script with same category, or with basic category.

As parameter metody evaluate in scriptEvaluator is added Builder. Only required parameter is code of script.

scriptEvaluator.evaluate(
    scriptEvaluator.newBuilder()
        .setScriptCode('SCRIPT_CODE_001')
        .addParameter('scriptEvaluator', scriptEvaluator)
        .addParameter('attributeValue', attributeValue)
        .addParameter('entity', entity)
        .addParameter('text', 'test')
	.build());

DefaultTransformToResourceEvaluator

DefaultTransformToResourceEvaluator - evaluator for transformation to system, can call script from category TRANSFORM_TO or DEFAULT. Evaluator is automatically added into transformation to system.

DefaultTransformFromResourceEvaluator

DefaultTransformFromResourceEvaluator - evaluator for transformation from system, can call script from category TRANSFORM_FROM or DEFAULT. Evaluator is automatically added into transformation from system.

DefaultScriptEvaluator

DefaultScriptEvaluator - Evaluator for DEFAULT category. Default skript can be called from another scripts.

Code for call script can be generated by frontend component script area (see first picture and button insert script). Modal window (picture second) is list of all available scripts.

For each script is allowed use logger. Example if log 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 message is required initialized 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");

For easily print some message to log can be used classic System.out.println or better print:

println("test")

The second one doesn't need another authorities.

Scripts agenda provides backup of all script that was write. For backup feature you must defined path. The path is defined by application property: idm.sec.core.backups.default.folder.path. Backup folder must have permission for read+write+execute, for user that run application server.

Script will be saved into this folder with this name: <scriptName>_<actualCzechIdMUser>_<timestamp>.xml

Script agenda also provides functionality for redeploy existing script. Only existing new script is deployed to IdM with application restart.

For redeploy you can use backup files. CzechIdM load all files in defined folder with 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 property: idm.sec.core.backups.default.folder.path. The property can contain classpath or folder. Example folder /opt/czechidm/backup/, example classpath: classpath*:/eu/bcvsolutions/idm/scripts/

If you use file not classpath, CzechIdM must have read+write+execute permission on folder that contains scripts and read permission on files.