Implementing ICF Operations With Groovy Scripts

The Groovy Connector Toolkit enables you to run a Groovy script for any ICF operation, such as search, update, create, and so forth, on any external resource.

You must write a Groovy script that corresponds to each operation that your connector will support. For information about all the operations that are supported by the ICF framework, see Implementing the ICF SPI.

Your scripted connector can implement the following ICF interfaces:

"Authenticate Operation"

Provides simple authentication with two parameters, presumed to be a user name and password.

"Create Operation"

Creates an object and its uid.

"Delete Operation"

Deletes an object, referenced by its uid.

"Resolve Username Operation"

Resolves an object to its uid based on its username.

"Schema Operation"

Describes the object types, operations, and options that the connector supports.

"Script On Connector Operation"

Enables IDM to run a script in the context of the connector. Any script that runs on the connector has the following characteristics:

  • The script runs in the same execution environment as the connector and has access to all the classes to which the connector has access.

  • The script has access to a connector variable that is equivalent to an initialized instance of the connector. At a minimum, the script can access the connector configuration.

  • The script has access to any script-arguments passed in by IDM.

"Script On Resource Operation"

Runs a script directly on the target resource that is managed by the connector.

"Search Operation"

Searches the target resource for all objects that match the specified object class and filter.

"Sync Operation"

Polls the target resource for synchronization events, that is, native changes to objects on the target resource.

"Test Operation"

Tests the connector configuration. Testing a configuration checks that all elements of the environment that are referred to by the configuration are available. For example, the connector might make a physical connection to a host that is specified in the configuration to verify that it exists and that the credentials that are specified in the configuration are valid.

This operation might need to connect to the resource, and, as such, might take some time. Do not invoke this operation too often, such as before every provisioning operation. The test operation is not intended to check that the connector is alive (that is, that its physical connection to the resource has not timed out).

You can invoke the test operation before a connector configuration has been validated.

"Update Operation"

Updates (modifies or replaces) objects on a target resource.

The following sections provide more information and pointers to sample scripts for all the operations that are implemented in the Groovy Connector Toolkit.

Variables Available to All Groovy Scripts

The following variables are available to all scripts used by the Groovy Connector. Additional variables are available to specific scripts, as described in the sections that follow:

configuration

A handle to the connector's configuration object is injected into all scripts.

operation

The connector injects the name of the action or operation into the script, to indicate which action is being called.

The sample scripts for the Groovy connector define one script file per action. You can use a single file, or amalgamate multiple actions into one file. For example, the CREATE and UPDATE operations often share the same code.

The operation type can be one of the following:

  • ADD_ATTRIBUTE_VALUES

  • AUTHENTICATE

  • CREATE

  • DELETE

  • GET_LATEST_SYNC_TOKEN

  • REMOVE_ATTRIBUTE_VALUES

  • RESOLVE_USERNAME

  • RUNSCRIPTONCONNECTOR

  • RUNSCRIPTONRESOURCE

  • SCHEMA

  • SEARCH

  • SYNC

  • TEST

  • UPDATE

options

The ICF framework passes an OperationOptions object to most of the operations. The Groovy connector injects this object, as is, into the scripts. For example, the search, query, and sync operations pass the attributes to get as an operation option.

The most common options are as follows:

  • AttributesToGet (String[]) for search and sync operations

  • RunAsUser (String) for any operation

  • RunWithPassword (GuardedString) for any operation

  • PagedResultsCookie (String) for search operations

  • PagedResultsOffset (Int) for search operations

  • PageSize (Int) for search operations

  • SortKeys (Sortkey[]) for search operations

objectClass

The category or type of object that is managed by the connector, such as ACCOUNT and GROUP.

log

A handle to the default ICF logging facility.

connection

Available to the ScriptedREST, ScriptedCREST, and ScriptedSQL implementations, this variable initiates the HTTP or SQL connection to the resource.

Writing an Authenticate Script

An authenticate script is required if you want to use pass-through authentication to the backend resource. If your connector does not need to authenticate to the resource, the authenticate script should allow the authId to pass through by default.

A sample authenticate script for an SQL database is provided in openidm/samples/scripted-sql-with-mysql/tools/AuthenticateScript.groovy

Input variables:

The following variables are available to the authenticate script:

configuration

A handler to the connector's configuration object.

options

A handler to the Operation Options.

operation

An OperationType that corresponds to the action (AUTHENTICATE).

objectClass

The object class being used to authenticate, such as __ACCOUNT__ or __GROUP__.

username

A string that provides the username to authenticate.

password

A guarded string that provides the password with which to authenticate.

log

A logger instance for the connector.

Returns: The user unique ID (ICF __UID__). The type of the returned UID must be a string or a Uid. The script must throw an exception in the case of failure.

Authenticate Script
def operation = operation as OperationType
def configuration = configuration as ScriptedConfiguration
def username = username as String
def log = log as Log
def objectClass = objectClass as ObjectClass
def options = options as OperationOptions
def password = password as GuardedString;

if (username.equals("TEST")) {
    def clearPassword = SecurityUtil.decrypt(password)
    if ("Passw0rd".equals(clearPassword)) {
        return new Uid(username);
    }
}    

Writing a Test Script

A test script tests the connection to the external resource to ensure that the other operations that are provided by the connector can succeed.

A sample test script for an SQL database is provided in openidm/samples/scripted-sql-with-mysql/tools/TestScript.groovy

Input variables:

The following variables are available to the test script:

configuration

A handler to the connector's configuration object.

operation

An OperationType that corresponds to the action (TEST).

log

A logger instance for the connector.

Returns: Nothing, if the test is successful. The script can throw any exception if it fails.

Test Script
import org.identityconnectors.common.logging.Log
 import org.forgerock.openicf.connectors.groovy.OperationType
 import org.forgerock.openicf.misc.scriptedcommon.ScriptedConfiguration

 def operation = operation as OperationType
 def configuration = configuration as ScriptedConfiguration
 def log = log as Log

 log.info("This is a TestScript")
 throw new MissingResourceException("Test Failed", operation.name(), "")
    

Writing a Create Script

A create script creates a new object on the external resource. If your connector does not support creating an object, this script should throw an UnsupportedOperationException.

A sample create script for an SQL database is provided in openidm/samples/scripted-sql-with-mysql/tools/CreateScript.groovy

Input variables:

The following variables are available to a create script:

configuration

A handler to the connector's configuration object.

options

A handler to the Operation Options.

operation

An OperationType that corresponds to the action (CREATE).

objectClass

The object class that is created, such as __ACCOUNT__ or __GROUP__.

attributes

The set of attributes that describe the object to be created.

id

The UID of the object to be created, if specified. If the UID is null, the UID should be generated by the server. The UID corresponds to the ICF __NAME__ attribute if it is provided as part of the attribute set.

log

A logger instance for the connector.

Returns: The user unique ID (ICF __UID__) of the newly created object. The type of the returned UID must be a string or a Uid. If a null value or an object type other than string or Uid is returned, the script must throw an exception.

Create Script
def operation = operation as OperationType
 def configuration = configuration as SapConfiguration
 def log = log as Log
 def objectClass = objectClass as ObjectClass
 def createAttributes = new AttributesAccessor(attributes as Set<Attribute>)
 def name = id as String
 def options = options as OperationOptions

 log.info("Entering {0} script",operation);


 assert operation == OperationType.CREATE, 'Operation must be a CREATE'
 // We only deal with users
 assert objectClass.getObjectClassValue() == ObjectClass.ACCOUNT_NAME


 def password = createAttributes.getPassword() as GuardedString;
 assert password != null, 'Password must be provided on create'


 //...
 def uid = createTheUser(createAttributes);
 return uid    

Writing an Update Script

An update script updates an object in the external resource. Connectors that do not support update operations should throw an UnsupportedOperationException.

A sample update script for an SQL database is provided in openidm/samples/scripted-sql-with-mysql/tools/UpdateScript.groovy

Input variables:

The following variables are available to an update script:

configuration

A handler to the connector's configuration object.

options

A handler to the Operation Options.

operation

An OperationType that corresponds to the action (UPDATE).

objectClass

The object class that is updated, such as __ACCOUNT__ or __GROUP__.

attributes

A collection of ConnectorAttributes that represent the entry attributes to update.

uid

The UID of the object to be updated. The UID corresponds to the OpenICF UID attribute.

id

The name of the object to be updated (optional). The id corresponds to the ICF __NAME__ attribute. It will not be injected and set unless the update is a rename.

log

A logger instance for the connector.

Returns: The user unique ID (ICF __UID__) of the updated object. The type of the returned UID must be a string or a Uid. If the UID is not modified by the update operation, return the value of the uid injected into the script.

Update Script
def operation = operation as OperationType
def updateAttributes = attributes as Set<Attribute>
def configuration = configuration as ScriptedConfiguration
def id = id as String
def log = log as Log
def objectClass = objectClass as ObjectClass
def options = options as OperationOptions
def uid = uid as Uid

log.ok("Update...")
switch (operation) {
    case OperationType.UPDATE:
        switch (objectClass) {
            case ObjectClass.ACCOUNT:
// ...
                    for (Attribute a : updateAttributes) {
                        if (a.is(Name.NAME)) {
// ...
return uid

Writing a Delete Script

A delete script deletes an object in the external resource. Connectors that do not support delete operations should throw an UnsupportedOperationException.

A sample delete script for an SQL database is provided in openidm/samples/scripted-sql-with-mysql/tools/DeleteScript.groovy

Input variables:

The following variables are available to an update script:

configuration

A handler to the connector's configuration object.

options

A handler to the Operation Options.

operation

An OperationType that corresponds to the action (DELETE).

objectClass

The object class that is deleted, such as __ACCOUNT__ or __GROUP__.

uid

The UID of the object to be deleted. The UID corresponds to the OpenICF __UID__ attribute.

log

A logger instance for the connector.

Returns: This script has no return value but should throw an exception if the delete is unsuccessful.

Writing a Synchronization Script

A synchronization script synchronizes objects between two resources. The script should retrieve all objects in the external resource that have been updated since some defined token.

A sample synchronization script for an SQL database is provided in openidm/samples/scripted-sql-with-mysql/tools/SyncScript.groovy

Input variables:

The following variables are available to a sync script:

configuration

A handler to the connector's configuration object.

options

A handler to the Operation Options.

operation

An OperationType that corresponds to the action (GET_LATEST_SYNC_TOKEN or SYNC).

objectClass

The object class that is synchronized, such as __ACCOUNT__ or __GROUP__.

token

The value of the sync token.

handler

A Closure handler for processing the sync results.

log

A logger instance for the connector.

Returns:

If the operation type is GET_LATEST_SYNC_TOKEN, the script must return an object that represents the last known SyncToken for the corresponding ObjectClass. For example:

def operation = operation as OperationType
def configuration = configuration as ScriptedConfiguration
def log = log as Log
def objectClass = objectClass as ObjectClass
def options = options as OperationOptions
def token = token as Object

case OperationType.GET_LATEST_SYNC_TOKEN:
        switch (objectClass) {
            case ObjectClass.ACCOUNT:
                return new SyncToken(17);
            case ObjectClass.GROUP:
                return new SyncToken(16);
            case ObjectClass.ALL:
                return new SyncToken(17);
// ....

If the operation type is SYNC, the script must return a new SyncToken for the corresponding ObjectClass. A Sync result handler (callback) is passed to the script to return the Sync results one by one. The handler must be called for each result.

The handler variable that is passed to the script is a Groovy Closure. It can be called in the following ways:

  • With an ICF SyncDelta object.

    You can use a SyncDeltaBuilder to build this object. For example:

    def builder = new SyncDeltaBuilder()
    builder.setUid(uidValue)
    builder.setToken(new SyncToken(5))
    builder.setDeltaType(SyncDeltaType.CREATE)
    builder.setObject(connectorObject)  // Use the ConnectorObjectBuilder class to build the ConnectorObject object.
    
    // Call the handler with the SyncDelta object
    handler builder.build()
  • Using a Groovy Closure.

    In this case, the Closure delegates calls to a specific Object that implements these calls. For example:

    handler {                       // The handler parameter here
                                                                              is a Closure
        syncToken tokenValue        // (mandatory), the method resolution for 'syncToken' is delegated to
                                       the Object handling the Closure
        <DELTA_TYPE>()              // (mandatory), DELTA_TYPE should be one of: CREATE, UPDATE, DELETE,
                                       CREATE_OR_UPDATE
        object connectorObject      // (optional if DELTA_TYPE is a DELETE), the method resolution for
                                       'object' is delegated to the Object handling the Closure
        previousUid prevUidValue    // (optional), use only if UID has changed
    }

    In the following example, the handler is called twice - first for a CREATE and then for a DELETE:

    // CREATE
    handler({
        syncToken 15
        CREATE()
        object {
            id nameValue
            uid uidValue as String
            objectClass ObjectClass.GROUP
            attribute 'gid', gidValue
            attribute 'description', descriptionValue
        }
    })
    
    // DELETE
    handler({
        syncToken 16
        DELETE(uidValue)
    }

    Optionally, when the action is SYNC, you might want to return a SyncToken at the end of the script. This is a convenient way to update the sync token if no relevant sync events are found.

Writing a Schema Script

A schema script builds the schema for the connector, either from a static, predefined schema, or by reading the schema from the external resource. The script should use the builder object to create the schema.

A sample schema script for an SQL database is provided in openidm/samples/scripted-sql-with-mysql/tools/SchemaScript.groovy

Input variables:

The following variables are available to a sync script:

configuration

A handler to the connector's configuration object.

operation

An OperationType that corresponds to the action (SCHEMA).

builder

An instance of the ICFObjectBuilder. The schema() method should be called with a Closure parameter defining the schema objects.

For more information, see "Using the builder Parameter".

log

A logger instance for the connector.

Returns: This script has no return value.

Using the builder Parameter

The builder.schema() must call the delegates objectClass method and operationOptions method to define the schema.

Call the objectClass() method for each object type (account, group, and so on) that must be defined. Include the call to the following delegates:

  • type() - the name for this object type

  • attribute() - define a single attribute for this object type

  • attributes() - define multiple attribute for this object type

  • disable() - list the operations for which this object type is forbidden

The following example defines a simple ACCOUNT object type:

builder.schema({
    objectClass {
        type ObjectClass.ACCOUNT_NAME
        attribute OperationalAttributeInfos.PASSWORD
        attribute PredefinedAttributeInfos.DESCRIPTION
        attribute 'groups', String.class, EnumSet.of(MULTIVALUED)
        attributes {
            userName String.class, REQUIRED
            email REQUIRED, MULTIVALUED
            __ENABLE__ Boolean.class
            createDate  NOT_CREATABLE, NOT_UPDATEABLE
            lastModified Long.class, NOT_CREATABLE, NOT_UPDATEABLE, NOT_RETURNED_BY_DEFAULT
            passwordHistory String.class, MULTIVALUED, NOT_UPDATEABLE, NOT_READABLE, NOT_RETURNED_BY_DEFAULT
            firstName()
            sn()
        }
    }
}

Writing a Resolve Username Script

A resolve username script resolves an object to its uid based on its username.

A sample resolve username script for an SQL database is provided in openidm/samples/scripted-sql-with-mysql/tools/ResolveUsernameScript.groovy

Input variables:

The following variables are available to a resolve username script:

configuration

A handler to the connector's configuration object.

options

A handler to the Operation Options.

operation

An OperationType that corresponds to the action (RESOLVE_USERNAME).

objectClass

The object class for which the username is resolved, such as __ACCOUNT__ or __GROUP__.

username

A string that represents the username of the object.

log

A logger instance for the connector.

Returns: The user unique ID (ICF __UID__) of the object. The type of the returned UID must be a string or a Uid. If a null value or an object type other than string or Uid is returned, the script must throw an exception.

Resolve Username Script
def operation = operation as OperationType
def configuration = configuration as ScriptedConfiguration
def username = username as String
def log = log as Log
def objectClass = objectClass as ObjectClass
def options = options as OperationOptions
if (objectClass.is(ObjectClass.ACCOUNT_NAME)) {
    if (username.equals("TESTOK1")) {
        return new Uid("123")
    }
    throw new UnknownUidException();
}

Writing a Run On Resource Script

A run on resource script runs directly on the target resource that is managed by the connector.

A sample run on resource script for a connector that connects to DS over REST is provided in openidm/samples/scripted-rest-with-dj/tools/ScriptOnResourceScript.groovy

Input variables:

The following variables are available to a run on resource script:

configuration

A handler to the connector's configuration object.

options

A handler to the Operation Options.

operation

An OperationType that corresponds to the action (RUNSCRIPTONRESOURCE).

arguments

The arguments (Map) of the script (can be null).

log

A logger instance for the connector.

Returns: Any object that is returned by the script.

Run on Resource Script
import groovyx.net.http.RESTClient
import org.apache.http.client.HttpClient
import org.forgerock.openicf.connectors.scriptedrest.ScriptedRESTConfiguration
import org.forgerock.openicf.connectors.groovy.OperationType
import org.identityconnectors.common.logging.Log
import org.identityconnectors.framework.common.objects.OperationOptions

def operation = operation as OperationType
def configuration = configuration as ScriptedRESTConfiguration
def httpClient = connection as HttpClient
def connection = customizedConnection as RESTClient
def log = log as Log
def options = options as OperationOptions
def scriptArguments = scriptArguments as Map
def scriptLanguage = scriptLanguage as String
def scriptText = scriptText as String

Writing a Run On Connector Script

A run on connector script enables IDM to run a script in the context of the connector.

Input variables:

The following variables are available to a run on connector script:

configuration

A handler to the connector's configuration object.

options

A handler to the Operation Options.

operation

An OperationType that corresponds to the action (RUNSCRIPTONCONNECTOR).

arguments

The arguments (Map) of the script (can be null).

log

A logger instance for the connector.

Returns: Any object that is returned by the script.

Read a different version of :