Groovy scripts and their authorization

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:

  • Services - beans/services which implement interface ScriptEnabled. An instance of this service is put into the script and the script is able to call public methods of this service. Before the script definition is saved, it's checked if the service exists in allowed classes.
  • Classes - All available classes in java. Before the script definition is saved, it's checked if the class exists.

Allowed classes

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
Be careful: if there is no return statement at the end, the script will return the last initialized attribute.

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

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

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

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.

For list of basic usable services in all scripts please visit this page.

  • by kopro