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.
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.
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.UserConnectorObjectimport org.openiam.api.connector.user.response.TestProvisioningConnectorResponseimport org.openiam.base.ws.ResponseStatusimport org.openiam.connector.core.base.commands.AbstractCommandExecutorimport org.openiam.connector.core.base.exception.ConnectorExceptionclass TestScriptConnector extends AbstractCommandExecutor<UserConnectorObject, TestProvisioningConnectorResponse> {@OverrideTestProvisioningConnectorResponse 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.CollectionUtilsimport org.apache.commons.logging.Logimport org.apache.commons.logging.LogFactoryimport org.openiam.api.connector.model.*import org.openiam.api.connector.user.response.SearchUserProvisioningConnectorResponseimport org.openiam.base.AttributeOperationEnumimport org.openiam.base.ws.ResponseStatusimport org.openiam.common.beans.jackson.CustomJacksonMapperimport org.openiam.connector.core.base.commands.AbstractCommandExecutorimport org.openiam.connector.core.base.exception.ConnectorExceptionimport org.springframework.context.ApplicationContextclass SearchScriptConnector extends AbstractCommandExecutor<ConnectorObject, SearchUserProvisioningConnectorResponse> {private static final Log log = LogFactory.getLog(SearchScriptConnector.class);private ApplicationContext context@OverrideSearchUserProvisioningConnectorResponse 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.userimport org.apache.commons.logging.Logimport org.apache.commons.logging.LogFactoryimport org.openiam.api.connector.model.ConnectorObjectimport org.openiam.api.connector.model.ConnectorObjectMetaDataimport org.openiam.api.connector.model.StringConnectorAttributeimport org.openiam.api.connector.user.response.SaveUserProvisioningConnectorResponseimport org.openiam.base.ws.ResponseStatusimport org.openiam.connector.core.base.commands.AbstractCommandExecutorimport org.openiam.connector.core.base.exception.ConnectorErrorCodeimport org.openiam.connector.core.base.exception.ConnectorExceptionimport org.springframework.context.ApplicationContextimport java.sql.*class SaveScriptConnector extends AbstractCommandExecutor<ConnectorObject, SaveUserProvisioningConnectorResponse> {private static final Log log = LogFactory.getLog(SaveScriptConnector.class);private ApplicationContext context@OverrideSaveUserProvisioningConnectorResponse 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().valuebreak;case "attribute2":attribute2 = att.getValues().first().valuebreak;}}}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;}}