Groovy script connector

The groovy script connector provides flexibility to support various types of communication with target systems, if out of the box connector is not available by OpenIAM.

Connector overview

Connector provides ability to call groovy script on each provisioning operation (save, search, test connection, delete, resume, suspend, reset password). Login is not supported so far. For each provisioning event, developer should develop a groovy script that extends AbstractCommandExecutor. There are no restrictions on the communications ways associated with the target system. Most common are REST API calls, PL/SQL package calls, shell script invocation. Connector as all other connectors can be started in remote mode. In this case, please note that groovy files should be located on server where connector is running, path to files should start from /data/openiam/conf/iamscripts/.

Configure OpenIAM

Configure provisioning connector

There is an out-of-the-box connector, called 'Groovy script connector', you can find in it list by navigating to webconsole->provisioning->connectors. Edit this connector by going into menu connector configuration. Enable rules that are necessary. Our advice, if you are going to use script connector in synchronization then always have Test connection object rule enabled along with script provided because otherwise synchronization process can’t be started (sync will throw an error about fail connection to target system). Due to current legacy state please consider that save handler should be used in both ‘add’ and ‘modify’ rules. Groovy script connector handlers

Configure managed system

You can use fields like 'login', 'password', 'host URL', 'port' 'connection string' etc. to store secured connection details, and obtain those in groovy script handler for any operation by:

final String login = connectorObject.getMetaData().getLogin()
final String password = connectorObject.getMetaData().getPassword()
final String hostURL = connectorObject.getMetaData().getUrl()
final Integer port = connectorObject.getMetaData().getPort()
final String connectionString = ConnectorObjectUtils.readMetadataAttributeValue(connectorObject.getMetaData(), ProvisionConnectorConstant.CONNECTION_STRING);

Fulfill the rule fields; each filed should contain path to a proper operation handler script. Groovy script managed system

Script development

Note, that groovy script connector caches groovy scripts on start. If you made a change in one of the handler groovy scripts you should restart connector service.

Test connection operation handler

This script is important to keep status of connection on the managed system dashboard. If there is no test connection handler presented, then you won't be able to start synchronization (because before start sycnhorniztion service checks status of connection on managed system dashboard). In case your system doesn't support test connection you can simply send success response, without really checking on the connection. Please find example of script in Appendix 1.

Search operation handler

Idea of this groovy script is to obtain data from source (ex.: JSON response of API) and transform it into response with connector objects. OpenIAM will receive this response and use it in synchronization. Please find example of script in Appendix 2.

CRUD operation handler

CRUD operations are 'save' and 'delete'. These scripts are going to use values that OpenIAM produces based on policy map of the managed system. Please find example of script in Appendix 3.

Appendixes

Disclaimer: following code examples are here only for reference, it should be refactored by developer to match your requirements.

1 Test connection groovy handler

import org.openiam.api.connector.model.UserConnectorObject
import org.openiam.api.connector.user.response.TestProvisioningConnectorResponse
import org.openiam.base.ws.ResponseStatus
import org.openiam.connector.core.base.commands.AbstractCommandExecutor
import org.openiam.connector.core.base.exception.ConnectorException
class TestScriptConnector extends AbstractCommandExecutor<UserConnectorObject, TestProvisioningConnectorResponse> {
@Override
TestProvisioningConnectorResponse perform(UserConnectorObject userConnectorObject) throws ConnectorException {
TestProvisioningConnectorResponse rt = new TestProvisioningConnectorResponse();
rt.setStatus(ResponseStatus.SUCCESS);
return rt;
}
}

2 Search user groovy handler

import org.apache.commons.collections4.CollectionUtils
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.openiam.api.connector.model.*
import org.openiam.api.connector.user.response.SearchUserProvisioningConnectorResponse
import org.openiam.base.AttributeOperationEnum
import org.openiam.base.ws.ResponseStatus
import org.openiam.common.beans.jackson.CustomJacksonMapper
import org.openiam.connector.core.base.commands.AbstractCommandExecutor
import org.openiam.connector.core.base.exception.ConnectorException
import org.springframework.context.ApplicationContext
class SearchScriptConnector extends AbstractCommandExecutor<ConnectorObject, SearchUserProvisioningConnectorResponse> {
private static final Log log = LogFactory.getLog(SearchScriptConnector.class);
private ApplicationContext context
@Override
SearchUserProvisioningConnectorResponse perform(ConnectorObject request) throws ConnectorException {
SearchUserProvisioningConnectorResponse rt = new SearchUserProvisioningConnectorResponse();
CustomJacksonMapper customJacksonMapper = context.getBean(CustomJacksonMapper.class)
ConnectorObjectMetaData meta = request.getMetaData();
List<StringConnectorAttribute> attributes = new ArrayList<>()
attributes.addAll(meta.getAttributes())
String searchFilter = ""
for (StringConnectorAttribute attribute : attributes) {
if ("searchQuery".equalsIgnoreCase(attribute.getName())) {
searchFilter = attribute.getValues().get(0).getValue()
break
}
}
final String WEB_SERVICE_URL = String.format("https://example.com/query/users/%s", searchFilter)
URL obj = new URL(WEB_SERVICE_URL);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Accept", "application/json");
int responseCode = con.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(
con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = reader.readLine()) != null) {
response.append(inputLine);
}
reader.close();
List<Object> resultList = new ArrayList<>()
/**
* create resultList from JSON data, using customJacksonMapper
*/
if (CollectionUtils.isNotEmpty(resultList)) {
List<UserConnectorObject> userConnectorObjects = new ArrayList<>()
resultList.forEach({ Object it ->
StringConnectorAttribute extAttr = null;
UserConnectorObject userConnectorObject = new UserConnectorObject();
userConnectorObject.setIdentityName("username")
userConnectorObject.setIdentityValue(it.getUserName())
userConnectorObject.setAttributes(new ArrayList<StringConnectorAttribute>());
extAttr = new StringConnectorAttribute("sourceAttributeName");
extAttr.addValue(new StringOperationalConnectorValue(String.valueOf(it.getValue()), AttributeOperationEnum.NO_CHANGE));
userConnectorObject.getAttributes().add(extAttr);
userConnectorObjects.add(userConnectorObject)
})
rt.setUserList(userConnectorObjects)
}
} else {
rt.setStatus(ResponseStatus.FAILURE);
}
rt.setStatus(ResponseStatus.SUCCESS);
return rt;
}
static class JSONResponse {
/**
* fields
*/
/**
* setters/getters
*/
}
}

3 Save user groovy handler

package org.openiam.connector.script.user
import org.apache.commons.logging.Log
import org.apache.commons.logging.LogFactory
import org.openiam.api.connector.model.ConnectorObject
import org.openiam.api.connector.model.ConnectorObjectMetaData
import org.openiam.api.connector.model.StringConnectorAttribute
import org.openiam.api.connector.user.response.SaveUserProvisioningConnectorResponse
import org.openiam.base.ws.ResponseStatus
import org.openiam.connector.core.base.commands.AbstractCommandExecutor
import org.openiam.connector.core.base.exception.ConnectorErrorCode
import org.openiam.connector.core.base.exception.ConnectorException
import org.springframework.context.ApplicationContext
import java.sql.*
class SaveScriptConnector extends AbstractCommandExecutor<ConnectorObject, SaveUserProvisioningConnectorResponse> {
private static final Log log = LogFactory.getLog(SaveScriptConnector.class);
private ApplicationContext context
@Override
SaveUserProvisioningConnectorResponse perform(ConnectorObject userConnectorObject) throws ConnectorException {
SaveUserProvisioningConnectorResponse response = new SaveUserProvisioningConnectorResponse();
Connection connection = getConnection(userConnectorObject.getMetaData())
CallableStatement callableStatement;
response.setStatus(ResponseStatus.SUCCESS);
String userName = userConnectorObject.getIdentityValue();
String attribute1;
String attribute2;
List<StringConnectorAttribute> attrList = new ArrayList<StringConnectorAttribute>()
attrList.addAll(userConnectorObject.getAttributes())
for (StringConnectorAttribute att : attrList) {
if (att.getName() != null) {
//here "username", "attribute1","attribute2" - are names of policies from policy map for user object.
switch (att.getName()) {
case "attribute1":
attribute1 = att.getValues().first().value
break;
case "attribute2":
attribute2 = att.getValues().first().value
break;
}
}
}
try {
ResultSet resultSet;
callableStatement = connection
.prepareCall("{call DB.dbo.Procedure_name(?,?,?)}");
callableStatement.setString(1, userName);
callableStatement.setString(2, attribute1);
callableStatement.setString(3, attribute2);
callableStatement.registerOutParameter(4, Types.VARCHAR);
callableStatement.registerOutParameter(5, Types.VARCHAR);
callableStatement.execute();
String status = callableStatement.getString(6);
String message = callableStatement.getString(7);
if (status != null && !status.equalsIgnoreCase("SUCCESS")) {
response.setStatus(ResponseStatus.FAILURE)
response.setErrorText(String.format("Status: %s, message: %s", status, message)
}
} catch (SQLException e) {
log.error("Error was caught.")
log.error(e)
response.setStatus(ResponseStatus.FAILURE)
response.setErrorText(e.getLocalizedMessage())
} finally {
try {
if (callableStatement != null) {
callableStatement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return response;
}
protected Connection getConnection(ConnectorObjectMetaData metaData) throws ConnectorException {
Connection sqlCon;
try {
final String connectionString = metaData.getUrl()
final String jdbcDriver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
Class.forName(jdbcDriver);
sqlCon = DriverManager.getConnection(connectionString, metaData.getLogin(), metaData.getPassword());
} catch (ClassNotFoundException ex) {
log.error(ex.getMessage(), ex);
throw new ConnectorException(ConnectorErrorCode.JDBC_DRIVER_NOT_FOUND, ex);
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
throw new ConnectorException(ConnectorErrorCode.UNABLE_TO_CONNECT, ex);
}
return sqlCon;
}
}