Implementing ICF Operations With Groovy Scripts
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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 itsusername
.- "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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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 operationsRunAsUser
(String) for any operationRunWithPassword
(GuardedString) for any operationPagedResultsCookie
(String) for search operationsPagedResultsOffset
(Int) for search operationsPageSize
(Int) for search operationsSortKeys
(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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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.
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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.
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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.
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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 a Search or Query Script
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
A search script searches for one or more objects on the external resource. Connectors that do not support searches should throw an UnsupportedOperationException
.
A sample search script for an SQL database is provided in openidm/samples/scripted-sql-with-mysql/tools/SearchScript.groovy
Input variables:
The following variables are available to the search 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 (
SEARCH
).- objectClass
The object class to search, such as
__ACCOUNT__
or__GROUP__
.- filter
The ICF native Query filter for this operation.
- query
A Map representation of the native Query filter that is easy to process.
Provides a convenient way to access the query filter parameter. For example:
query = [ operation: "CONTAINS", left: attribute, right: "value", not: true/false ] query = [ operation: "ENDSWITH", left: attribute, right: "value", not: true/false ] query = [ operation: "STARTSWITH", left: attribute, right: "value", not: true/false ] query = [ operation: "EQUALS", left: attribute, right: "value", not: true/false ] query = [ operation: "GREATERTHAN", left: attribute, right: "value", not: true/false ] query = [ operation: "GREATERTHANOREQUAL", left: attribute, right: "value", not: true/false ] query = [ operation: "LESSTHAN", left: attribute, right: "value", not: true/false ] query = [ operation: "LESSTHANOREQUAL", left: attribute, right: "value", not: true/false ] query = null : then we assume we fetch everything // AND and OR filter - embed these left/right queries: query = [ operation: "AND", left: query1, right: query2 ] query = [ operation: "OR", left: query1, right: query2 ]
For example, the equality query filter
"sn == Smith"
would be represented by the following query Map:[ operation: "EQUALS", left: "sn", right: "Smith", not: false ]
- handler
A Closure handler for processing the search results.
- log
A logger instance for the connector.
Returns: Optionally, the script can return a search result. The result can be be returned as a SearchResult
object or as a String
that represents the pagedResultsCookie
to be used for the next paged results.
Returning Search Results
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
In a search operation, a result handler (callback) is passed to the script to return the results one by one. The handler must be called for every query result. The handler variable that is passed to the script is a Groovy Closure
. You can call the handler in the following ways:
Using an ICF
ConnectorObject
object.You can use the
ConnectorObjectBuilder
to build this object. For example:def builder = new ConnectorObjectBuilder() builder.setUid(uidValue) builder.setName(nameValue) builder.setObjectClass(ObjectClass.ACCOUNT) builder.addAttribute("sn", snValue) // Call the handler with the ConnectorObject 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 uid uidValue // (mandatory), the method resolution for 'uid' is delegated to the Object // handling the Closure. This is the ICF __UID__ id nameValue // (mandatory), the method resolution for 'id' is delegated to the Object // handling the Closure. This is the ICF __NAME__ attribute "sn", snValue // (optional), the method resolution for 'id' is delegated to the // Object handling the Closure // attribute <attribute2Name>, <attribute2Value> // etc... }
In the following example, the handler is called within a loop to return all the results of a query:
for (user in userList) { handler { uid user.userName id user.userName user.attributes.each(){ key,value -> attribute key, value } } }
Writing an Update Script
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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.
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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
orSYNC
).- 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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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
. Theschema()
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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
The builder.schema()
must call the delegates objectClass
method and operationOption
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 typeattribute()
- define a single attribute for this object typeattributes()
- define multiple attribute for this object typedisable()
- 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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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.
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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 benull
).- log
A logger instance for the connector.
Returns: Any object that is returned by the script.
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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
Important
Connectors continue to be released outside the IDM release. For the latest documentation, refer to the ICF documentation.
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 benull
).- log
A logger instance for the connector.
Returns: Any object that is returned by the script.