lab 17: create an oim flat file connector using …...lab 17: create an oim flat file connector...

83
Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class Lab 17: Create an OIM Flat file connector using Identity Connector Framework (ICF) Disclaimer: The Virtual Machine Image and other software are provided for use only during the workshop. Please note that you are responsible for deleting them from your computers before you leave. If you would like to try out any of the Oracle products, you may download them from the Oracle Technology Network (http://www.oracle.com/technology/index.html ) or the Oracle E-Delivery WebSite (http://edelivery.oracle.com )

Upload: others

Post on 20-Jun-2020

18 views

Category:

Documents


0 download

TRANSCRIPT

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Lab 17: Create an OIMFlat file connector using Identity Connector Framework (ICF)

Disclaimer: The Virtual Machine Image and other software are provided for use only

during the workshop. Please note that you are responsible for deleting them from your

computers before you leave. If you would like to try out any of the Oracle products, you

may download them from the Oracle Technology Network

(http://www.oracle.com/technology/index.html) or the Oracle E-Delivery WebSite

(http://edelivery.oracle.com)

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Table of Contents 1. Introduction

1.1. Lab stories and story lines 1.2. ICF Architecture

1.2.1. Overview 1.2.2. Connector API

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

1.2.2.1. ICF common glue for OIM 1.2.3. Connector SPI

2. ICF Features 2.1. Connector JARs 2.2. Connection Pooling and Time outs 2.3. Remote Connector Server 2.4. Common objects

3. Create Java application and Projects in JDeveloper 3.1. FlatFileConnectorApplication and FlatFileConnectorProject 3.2. FlatFileConnectorTestProject

4. Flat File Connector 4.1. Create FlatFileConnectorConfiguration 4.2. Create FlatFileConnector 4.3. Test for successful connection with target

5. Provisioning 5.1. Introduction 5.2. Provisioning operations

5.2.1. Create 5.2.2. Update 5.2.3. Delete 5.2.4. Enable 5.2.5. Disable 5.2.6. Testing

5.2.6.1. Testing Create operation 5.2.6.2. Testing Update operation (includes update, enable and disable) 5.2.6.3. Testing Delete operation

6. Reconciliation 6.1. Introduction 6.2. Reconciliation using Search operation

6.2.1. SearchOp interface 6.2.2. Filter Translator 6.2.3. Results Handler 6.2.4. Operation Options

6.3. One time reconciliation 6.4. Incremental reconciliation 6.5. Lookup reconciliation 6.6. Role reconciliation 6.7. Test

7. Advanced operations 7.1. Provisioning using scripts 7.2. Reconciliation using scripts 7.3. Extending the functionality of the connector

7.3.1. Validation 7.3.2. Transformation

8. Conclusion

1. Introduction

1.1 Lab stories and story lines ACME CAPITAL is all set to extend the provisioning solution to accommodate extra applications coming onboard. Some application does not have an out of the box connectors from Oracle. ACME Capital has decided to create custom connector for this target. ACME capital needs to develop the custom connectors in two different phases:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

A. Phase 1: Develop Connector Bundle which requires only ICF SPI and Target System API knowledge. ACME Capital developers can independently develop this connector bundle without having the need to have fully fledged OIM Deployment

B. Phase 2: Develop OIM Metadata so that end to end process of data flow from OIM to Target and

Target to OIM can be achieved.

1.1 ICF Architecture

1.2.1 Overview ICF is designed to decouple any dependency of OIM with the target application that it is connecting to. ICF Connectors allow OIM to carry out user provisioning/reconciliation operations on target systems in the enterprise. The interaction between OIM and the target application via the ICF is depicted in the below diagram. ICF itself is divided into two parts.

Connector API

Connector SPI

1.2.2 Connector API

Connector API provides a consistent view of any connector regardless of the operations supported by it. APIs help OIM to communicate with the Connector and vice versa. To achieve this it provides interfaces and classes which are discussed below:

org.identityconnectors.framework.api.ConnectorInfoManagerFactory –

This is the entry point for OIM into connectors and helps to load

a connector classes from a set of bundles.

org.identityconnectors.framework.api.ConnectorInfoManager – This

maintains a list of ConnectorInfo instances, each of which

describes a connector that is available.

org.identityconnectors.framework.api.ConnectorInfo – This is the

meta-data for a particular connector.

org.identityconnectors.framework.api.ConfigurationProperties –

This encapsulates the

org.identityconnectors.framework.spi.Configuration at the SPI

layer and helps application to set/update connector configuration

properties.

org.identityconnectors.framework.api.ConnectorFacadeFactory –

Allows OIM to get a connector instance and also manages pool of

instances.

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

org.identityconnectors.framework.api.ConnectorFacade – Main

interface through which application invokes connector operation.

Represents a connector instance at the API level.

1.2.2.1 ICF glue for OIM

OIM provides the ICF API implementation for connectors. This is in the form of a JAR file named icf-oim-intg.jar and can be found at $DW_HOME/server/icf/intg/ Connector developers should make use of this API implementation to glue the connector SPI implementation with OIM. This JAR provides many classes and interfaces which can be used by OIM adapters or scheduled tasks/jobs to perform various provisioning and reconciliation operations. Below are couple of important classes and their APIs:

oracle.iam.connectors.icfcommon.prov.ICProvisioningManager – This

is the common provisioning manager and provides many APIs for

various provisioning actions. Each of them have been discussed

below:-

o public ICProvisioningManager(String itResourceFieldName,

long processInstanceKey, tcDataProvider dataProvider) –

Constructor. This takes a reference to the IT resource

field name as defined in the form, process instance key

value and a reference to the database.

o public String createObject(String objectType) – Method

responsible for calling create API on the connector

implementation. Takes type of the object being created

(User etc...). This method creates a set of

org.identityconnectors.framework.common.objects.Attribute

objects, where each Attribute represents the name and

values of the columns on the user form and passes it onto

the create method of the connector implementation. This

returns either ‘SUCCESS’ or ‘ERROR’ based on the operation.

o public String deleteObject(String objectType) – Method

responsible for calling the delete API on the connector

implementation. Takes type of the object being created

(User etc...). This API finds out the Uid of the object

being deleted and passes it onto delete API of connector

implementation and returns either ‘SUCCESS’ or ‘ERROR’

based on the operation.

o public String updateAttributeValue(String objectType,

String attrFieldName) – Method responsible for calling the

update API on the connector implementation. Takes the type

of the Object being updated and the field name being

updated. This creates a set a new Attributes which will

hold the updated values of this field and passes it on to

update method of the connector implementation and returns

either ‘SUCCESS’ or ‘ERROR’ based on the operation.

o public String updateChildTableValues(String objectType,

String childTableName) – Method responsible for calling

update API on connector implementation. This method should

be called whenever there is an insert/update/delete of

field values belonging to child table.

o public String enableUser() – Method responsible to call

update API on the connector implementation. This should be

called when a user gets enabled on the OIM side.

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

o public String disableUser() - Method responsible to call

update API on the connector implementation. This should be

called when a user gets disabled on the OIM side.

oracle.iam.connectors.icfcommon.recon.SearchReconTask – This is a

scheduler based task and provides implementation for

reconciliation operations and by calling the search API on the

connector implementation.

o protected void execute() – This is the implementation of

com.thortech.xl.scheduler.tasks.SchedulerBaseTask.execute

and calls the search API on connector implementation. The

same method can be used to perform full, incremental and

lookup reconciliation. The scheduled task parameters are

passed onto connecter search API using the OperationOptions

parameter. Based on the values in

OperationOptions(scheduled task parameters), different

implementations can be provided on the connector search

implementation.

For more information on how to use these APIs in OIM, please see subsequent labs

1.2.3 Connector SPI

Connector SPI provides various interfaces using which the Connector can be developed. This can again be classified into required, operation and feature based interfaces. Required interfaces must be implemented regardless of the operations supported and they help to create the Connector and maintain the connection with the target system, while operation interfaces helps the connector to support various operations. Feature based interfaces support certain features supported by the ICF. Interfaces used in this lab are shown below and their APIs are explained in detail in subsequent sections.

org.identityconnectors.framework.spi.Connector – Main required

interface which helps the SPI developer to implement the Connector

org.identityconnectors.framework.spi.Configuration – Required

interface which holds all the necessary information of the target

system, using which the Connector actually makes a connection to

the target system.

org.identityconnectors.framework.spi.operations.CreateOp –

Operation interface which helps to create information on the

target system

org.identityconnectors.framework.spi.operations.UpdateOp –

Operation interface which helps to update existing information on

the target system

org.identityconnectors.framework.spi.operations.DeleteOp –

Operation interface which helps to delete information from the

target system

org.identityconnectors.framework.spi.operations. SearchOp<T> -

Operation interface which helps to perform a search operation on

the target system. This is used to perform full, lookup

reconciliation.

org.identityconnectors.framework.spi.PoolableConnector – Feature

based interface which helps creating a connector, while the ICF

framework helps maintain the connector instance pooling.

2. ICF Features

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

2.1 Connector JARs – ICF provides two JARs namely connector-framework.jar and connector-

framework-internal.jar. The connector-framework.jar is a compile time JAR and is required while developing the connector, while connector-framework-internal.jar is a run time JAR and is required during run time i.e. while we are using the connector for provisioning or reconciliation operations.

2.2 Connection Pooling and Time outs – This is a feature provided by the ICF where in the

framework would maintain a pool of connector instances and uses them while performing provisioning and recon operations. Connectors can make use of pooling by implementing PoolableConnector instead of plain Connector interface. For the SPI developer to make use of this feature, all needs to be done are to implement the PoolableConnector.

If the SPI developer chooses to implement Connector interface, ICF for every operation would create a new connector instance, creates a new connection with the target and completes provisioning/recon operation, removes the connection with the target and finally disposes this connector instance.

As we can see, advantages of implementing PoolableConnector is that a pool of connector instances (these are configurable) are maintained and are re-used for many operations.

Some of configurable options are:

Maximum connector objects in the pool which are idle and active (_maxObjects)

Maximum connector objects which are idle (_maxIdle)

Max time to wait if the pool is waiting for a free object to become available before failing (_maxWait)

Minimum time to wait before evicting an idle object. (_minEvictableIdleTimeMillis)

Minimum number of idle objects. (_minIdle)

These values can be set by OIM using a special lookup (see metadata for more information) and if not provided following default values will be used.

_maxObjects = 10

_maxIdle = 10

_maxWait = 150 * 1000 ms

_minEvictableIdleTimeMillis = 120 * 1000 ms

_minIdle = 1

These are the optimal values. See section 4.3.5 on how to set these values.

2.3 Remote Connector server - Connector servers are required when we want to execute

connector bundle outside of the application (OIM). Connector servers are available for both java and .NET. These can be downloaded from OTN. In this lab, we use basic configurations, for more details, please

seehttps://wikis.oracle.com/display/IdentityConnectors/Connector+Servers

Connector_Server_111200.zip has been downloaded from

http://download.oracle.com/otn/nt/ias/connectors/111/Connector_Server_111200.zip (Select Connector Server) in /app/software

Copy the file in /app: cp /app/software/Connector_Server_111200.zip /app

in /app, unzip the file (it creates folder Connector_Server_111200)

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Make sure that you have a directory connector_server_java-1.4.0 inside the extracted Connector_Server_111200 folder. Note: if the directory is not there you can unzip the zip file ConnectorServer-1.4.0.0.zip

The connector server directory structure is as shown below:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Copy from /app/dummydata/Lab 17/jars sshfactory.jar in /app/Connector_Server_111200/ connector_server_java-1.4.0 /lib

Change the permission for the executable: chmod 777 connectorserver.sh located in /app/Connector_Server_111200/connector_server_java-1.4.0/bin

Before using the connector server, we need to set the key to be used by both the connector server and OIM. This key is required by any client that connects to this connector server and has to be done one time only. To set the key and start the connector server, navigate to the $CONNECTOR_SERVER_HOME/bin directory (in our virtual machine: /app/Connector_Server_111200/connector_server_java-1.4.0/bin)

On Windows platform:

To set key, run ConnectorServer /setkey newkey where newkey is the value for the new key

To start the connector server, run ConnectorServer /run

On Linux/UNIX platform:

To set key, run sh connectorserver.sh /setKey newKey where newkey is the value for the new key

To start the connector server, run sh connectorserver.sh /run

In our lab virtual machine:

[oracle@identity bin]$ sh connectorserver.sh /setKey 12345 CONNECTOR_SERVER_HOME: /app/Connector_Server_111150/connector_server_java-1.2.6195 Using JAVA_HOME: /app/jdk Using JRE_HOME: /app/jdk [oracle@identity bin]$ sh connectorserver.sh /run CONNECTOR_SERVER_HOME: /app/Connector_Server_111150/connector_server_java-1.2.6195 Using JAVA_HOME: /app/jdk Using JRE_HOME: /app/jdk

Aug 10, 2012 5:02:57 AM org.identityconnectors.framework.server.Main buildBundleURLs WARNING: No bundles found in the bundles directory Aug 10, 2012 5:02:57 AM org.identityconnectors.framework.server.impl.CCLWatchThread checkCCL INFO: Creating thread 'ConnectionListener' with a non-null CCL Aug 10, 2012 5:02:57 AM org.identityconnectors.framework.server.Main run INFO: Connector server listening on port 8759

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Note:

The user, who has logged into system which has the connector server, needs to have permission to run the ConnectorServer.bat in case of windows or connectorserver.sh in case of Linux/UNIX.

To stop the Connector Server, just control-C in the terminal window.

The above steps shown are for only Java connector server, for information on how to install, set key and running the .NET connector server, please see

https://wikis.oracle.com/display/IdentityConnectors/Connector+Servers

2.4 Common objects - There are many classes utilized by the ICF to communicate from API to

SPI and vice-versa. These objects help ICF to communicate during operations being performed on the target application. A few very important common classes/objects have been described below.

org.identityconnectors.framework.common.objects.Attribute – An

Attribute is nothing but named collection of values within a

target object. i.e A target object can have many attributes and

each attribute can have many values. In the simplest form, an

attribute can be considered as a name-value pair of a target

object. Empty and null values are also supported. Note: Connector

developer should make use of

org.identityconnectors.framework.common.objects.AttributeBuilder

to construct Attribute instances.

org.identityconnectors.framework.common.objects.Uid – A single

valued Attribute is Uid. When an object is created because of a

create operation, it returns with a UID. This UID actually refers

to the target object created i.e it is a unique reference to the

target object and if possible should be immutable.Note: UID is a

sub class of Attribute

org.identityconnectors.framework.common.objects.ObjectClass – An

object class defines the type or category of the object on the

target resource. Some of the object classes defined are ACCOUNT,

GROUP.

org.identityconnectors.framework.common.objects.ConnectorObject –

A ConnectorObject represents an object on the target resource. For

example ACCOUNT or GROUP.

Note:

org.identityconnectors.framework.common.objects.ConnectorObjectBui

lder should be used to construct ConnectorObject.

org.identityconnectors.common.security.GuardedString – A guarded

string is a secure String implementation which solves the problem

of storing passwords in a plain String format in memory. Passwords

will be stored as Bytes in an encrypted format. The encryption key

will be randomly generated.

3. Create Java application and Projects in JDeveloper

Pre-requisite:

The remote connector server should be setup. Please complete steps as in section 2.3

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Remark: sshfactory.jar has been copied in lib directory of the connector server. The full product could be downloaded from

http://www.jscape.com/downloads/ssh-factory

3.1 FlatFileConnectorApplication and FlatFileConnectorProject – This lab makes use of

JDeveloper Generic Release Java Edition: 11.1.1.6.0 to develop and test the connector, which

can be found here http://www.oracle.com/technetwork/developer-tools/jdev/downloads/index.html. However there is no dependency on JDeveloper

versions.

Open JDeveloper. Select File -> New... Under General, select Applications and “Java Desktop Application” and click on OK.

Enter Application Name as “FlatFileConnectorApplication“ as shown below and click on Next.

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Enter “FlatFileConnectorProject” as Project Name. Select Ant, xml under Project Technologies and click Next.

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Clear the “Default Package” and click Finish.

Right click on FlatFileConnectorProject and click on “Project Properties”

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Select “Libraries and Classpath” and click “Add JAR/Directory”

Select connector-framework.jar, connector-framework-internal.jar (from

/app/Connector_Server_111200/connector_server_java-1.4.0/lib/framework) and sshFactory.jar (from /app/Connector_Server_111200/connector_server_java-1.4.0/lib) and click OK

Click on Save (floppy disk icon).

3.2 FlatFileConnectorTestProject – We make use of a standalone java client to test the

connector and various operations it supports. For this create a new project under same application as shown below with name FlatFileConnectorTestProject. For now let it be empty.

Click on File -> New… and select Project -> Generic Project

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Name the project FlatFileConnectorTestProject and click on Finish

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Right Cliek on the FlatFileConnectorTestProject , Select “Libraries and Classpath” and click “Add JAR/Directory”

Select connector-framework.jar, connector-framework-internal.jar (from

/app/Connector_Server_111200/connector_server_java-1.4.0/lib/framework) and sshFactory.jar (from /app/Connector_Server_111200/connector_server_java-1.4.0/lib) and click OK

This will add the jars needed to compile the java files which will be added later.

4. Flat File Connector – In this section, we will create implementations for required interfaces

org.identityconnectors.framework.spi.Configuration and org.identityconnectors.framework.spi.Connector and test the connector implementation to see it can make successful connection with the target.

4.1 Create FlatFileConnectorConfiguration - Extending

org.identityconnectors.framework.spi.AbstractConfiguration - ICF provides a convenient base class AbstractConfiguration for configuration objects to extend. We will use this abstract class instead of implementing Configuration interface directly. Advantages include:

public abstract ConnectorMessages getConnectorMessages()

public abstract void setConnectorMessages(ConnectorMessages

paramConnectorMessages)

The above two methods have already been implemented. The only left method need to be implemented would the public abstract void validate(). The implementation of this method should validate all the properties, which are required for a connector to successfully connect to the target system, are available and complete.

We would use the following configuration properties in this lab, which will be used by the connector.

hostname of type java.lang.String --> hostname of the target

system.

username of type java.lang.String --> username with which we

connect to target system

password of type org.identityconnectors.common.security -->

password for the above user.

targetFile of type java.io.File --> flat file onto which we do

provisioning actions.

uniqueAttribute of type java.lang.String --> unique attribute

help us do provisioning actions.

Create the above private instance variables and generate corresponding accessors and annotate with ConfigurationProperty and provide an implementation for the public abstract void validate() like below: Right click on FlatFileConnectorProject, select New..., under General Categories, Select Java and “Java Class” on the right side of the dialog box and click OK

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Enter

FlatFileConnectorConfiguration for Name

org.identityconnectors.flatfileconnector for Package

org.identityconnectors.framework.spi.AbstractConfiguration for Extends

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Click OK.

Note: The package convention followed is org.identityconnectors.<ConnectorName>

Copy and paste the java code bellow and save the file

package org.identityconnectors.flatfileconnector;

import org.identityconnectors.framework.spi.AbstractConfiguration;

import org.identityconnectors.framework.spi.ConfigurationProperty;

import org.identityconnectors.common.security.*;

import java.beans.PropertyDescriptor;

import java.lang.reflect.Method;

import java.beans.IntrospectionException;

import java.lang.reflect.InvocationTargetException;

import org.identityconnectors.common.StringUtil;

import

org.identityconnectors.framework.common.exceptions.ConfigurationException;

import org.identityconnectors.common.logging.Log;

import java.beans.Introspector;

import java.io.File;

public class FlatFileConnectorConfiguration extends AbstractConfiguration {

private static final Log LOG =

Log.getLog(FlatFileConnectorConfiguration.class);

private File targetFile = null;

private String uniqueAttribute = null;

private File customScriptForProvisioning = null;

private File customScriptForRecon = null;

private String hostName = null;

private String userName = null;

private GuardedString password = null;

private File lookupReconFile = null;

public void setLookupReconFile(File lookupReconFile) {

System.out.println("lookupReconFile = "+lookupReconFile);

this.lookupReconFile = lookupReconFile;

}

@ConfigurationProperty(order = 1, required = true,helpMessageKey =

"configuration.lookupReconFile.help",

displayMessageKey =

"configuration.lookupReconFile.display")

public File getLookupReconFile() {

return lookupReconFile;

}

public void setCustomScriptForRecon(File customScriptForRecon) {

this.customScriptForRecon = customScriptForRecon;

}

@ConfigurationProperty(order = 1, required = false,helpMessageKey =

"configuration.customscriptfileforrecon.help",

displayMessageKey =

"configuration.customscriptfileforrecon.display")

public File getCustomScriptForRecon() {

return customScriptForRecon;

}

public void setCustomScriptForProvisioning(File

customScriptForProvisioning) {

this.customScriptForProvisioning = customScriptForProvisioning;

}

@ConfigurationProperty(order = 1, required = false,helpMessageKey =

"configuration.customscriptfileforprovisioning.help",

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

displayMessageKey =

"configuration.customscriptfileforprovisioning.display")

public File getCustomScriptForProvisioning() {

return customScriptForProvisioning;

}

public void setTargetFile(File targetFile) {

System.out.println("targetFile = "+targetFile);

this.targetFile = targetFile;

}

@ConfigurationProperty(order = 1, required = true,helpMessageKey =

"configuration.targetfile.help",

displayMessageKey =

"configuration.targetfile.display")

public File getTargetFile() {

return targetFile;

}

public void setUniqueAttribute(String uniqueAttribute) {

this.uniqueAttribute = uniqueAttribute;

}

@ConfigurationProperty(required =

true,helpMessageKey="configuration.uniqueattribute.help",

displayMessageKey="configuration.uniqueattribute.display")

public String getUniqueAttribute() {

return uniqueAttribute;

}

/**

* {@inheritDoc}

*/

@Override

public void validate() {

if(this.targetFile == null || this.uniqueAttribute == null){

throw new ConfigurationException("Required parameters are

null!!");

}

validateRequired();

}

public FlatFileConnectorConfiguration() {

// it's java bean

}

/**

*

* Validates that all required properties are in place.

*

* @throws ConfigurationException in case value of required property is

missing.

*/

private void validateRequired() {

try {

PropertyDescriptor[] propertyDescs =

Introspector.getBeanInfo(this.getClass()).getPropertyDescriptors();

for (PropertyDescriptor propertyDescriptor : propertyDescs) {

Method getter = propertyDescriptor.getReadMethod();

assert getter != null;

ConfigurationProperty annotation =

getter.getAnnotation(ConfigurationProperty.class);

if (annotation != null && annotation.required()) {

Object value = getter.invoke(this);

if (value == null) {

// missing required property

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

throw new

ConfigurationException(getMessage("validate.missingPropertyValue",

getMessage(annotation.displayMessageKey())));

}

else if (value instanceof String &&

StringUtil.isBlank((String)value)) {

// blank String

throw new

ConfigurationException(getMessage("validate.missingPropertyValue",

getMessage(annotation.displayMessageKey())));

}

}

}

} catch (IntrospectionException ex) {

// should not happen, just log it

LOG.warn(ex, "Error when validating required properties.");

} catch (IllegalAccessException ex) {

// should not happen, just log it

LOG.warn(ex, "Error when validating required properties.");

} catch (InvocationTargetException ex) {

// should not happen, just log it

LOG.warn(ex, "Error when validating required properties.");

}

}

/**

* Returns localized message.

*

* @param key Message key.

* @return Localized message.

*/

public String getMessage(String key) {

return getConnectorMessages().format(key, key);

}

/**

* Returns localized message.

*

* @param key Message key.

* @param objects Values to be set to message placeholders.

* @return Localized message.

*/

public String getMessage(String key, Object... objects) {

return getConnectorMessages().format(key, key, objects);

}

public void setHostName(String hostName) {

this.hostName = hostName;

}

@ConfigurationProperty(required =

true,helpMessageKey="configuration.hostName.help",displayMessageKey="configu

ration.hostName.display")

public String getHostName() {

return hostName;

}

public void setUserName(String userName) {

this.userName = userName;

}

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

@ConfigurationProperty(required =

true,helpMessageKey="configuration.userName.help",displayMessageKey="configu

ration.userName.display")

public String getUserName() {

return userName;

}

public void setPassword(GuardedString password) {

this.password = password;

}

@ConfigurationProperty(required =

true,helpMessageKey="configuration.password.help",displayMessageKey="configu

ration.password.display")

public GuardedString getPassword() {

return password;

}

}

Create Messages.properties file under the same package: Click on the package name org.identityconnectors.flatfileconnector, right-click on New... and select General on the left and File on the right of the dialog box. Click on OK, enter Messages.properties for the file name and click on OK again. Copy and paste the following content:

connector.display=FlatFileConnector Connector

# configuration properties:

# recommended format:

# configuration.<propertyName>.display=<displayName>

# configuration.<propertyName>.help=<helpDescription>

# configuration validation:

validate.missingPropertyValue=Value of "{0}" must be set.

configuration.targetfile.help=Target flat file to which provisioning would

be performed

configuration.targetfile.display=Target flat file

configuration.uniqueattribute.help=Unique attribue with which we identify

the account on the target file

configuration.uniqueattribute.display=Unique attribute

configuration.customscriptfileforprovisioning.help=Custom script used for

provisioning operation

configuration.customscriptfileforprovisioning.display=Custom script

configuration.customscriptfileforrecon.help=Custom script used for recon

operation

configuration.customscriptfileforrecon.display=Custom script used for recon

operation

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

configuration.hostName.help=Target hostname, hostname of the machine to/from

which provisioning and recon should be performed

configuration.hostName.display=Target hostname

configuration.userName.help=Username required to connect to target system

configuration.userName.display=Username

configuration.password.help=Password required to connect to target system

configuration.password.display=Password

configuration.lookupReconFile.help=Lookup recon file name

configuration.lookupReconFile.display=Lookup recon file

Click on “Save All”

4.2 Create FlatFileConnector – Implementing the PoolableConnector interface. The

PoolableConnector extends the Connector interface and if connector implementation implements the PoolableConnector, ICF framework would help us with the Connector object pooling, as described in section 2.2

public void init(Configuration configuration) - The main purpose of the init method is to create a

connection with the target system, using the target system information as provided the configuration information. So we could the following:

Store the configuration instance.

Create the connection with the target using the configuration information - While creating the connection, GuarderString needs to be used for password handling. See createSshConnection method implementation for more details.

Create a flag to indicate this connector has an active connection with the target public void dispose() - The main purpose of dispose method is to terminate/remove the connection with the target system.

Terminate the connection with the target system

Update the flag, to indicate that this connector has no more active connection with the target

public void checkAlive() - The main purpose of checkAlive method is to verify if this connector still has active connection with the target.

Based on the flag, if the connector does not have an active connector, throw an Exception.

Create another Java Class FlatFileConnector implementing PoolableConnection in package org.identityconnectors.flatfileconnector. You can have a look after copying the following java code to the implementations of the above methods:

public Configuration getConfiguration()

public void init(Configuration configuration)

public void dispose()

public void checkAlive() Notice also that the Connector has been annotated with @ConnectorClass.

package org.identityconnectors.flatfileconnector;

import com.jscape.inet.ssh.SshException;

import com.jscape.inet.ssh.SshSession;

import com.sun.corba.se.impl.io.InputStreamHook;

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

import java.io.BufferedReader;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Date;

import java.util.HashMap;

import java.util.HashSet;

import java.util.List;

import java.util.Map;

import java.util.Scanner;

import java.util.Set;

import org.identityconnectors.framework.common.objects.ResultsHandler;

import org.identityconnectors.framework.common.objects.Schema;

import org.identityconnectors.framework.common.objects.ScriptContext;

import

org.identityconnectors.framework.common.objects.filter.FilterTranslator;

import org.identityconnectors.framework.spi.Configuration;

import org.identityconnectors.framework.spi.Connector;

import org.identityconnectors.framework.spi.PoolableConnector;

import org.identityconnectors.common.logging.Log;

import org.identityconnectors.common.security.GuardedString;

import org.identityconnectors.framework.common.objects.Attribute;

import org.identityconnectors.framework.common.objects.ObjectClass;

import org.identityconnectors.framework.common.objects.OperationOptions;

import org.identityconnectors.framework.common.objects.Uid;

import org.identityconnectors.flatfileconnector.util.FlatFileUtil;

import org.identityconnectors.framework.common.objects.AttributeBuilder;

import org.identityconnectors.framework.common.objects.AttributeInfo;

import org.identityconnectors.framework.common.objects.AttributeInfoBuilder;

import org.identityconnectors.framework.common.objects.ConnectorObject;

import

org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;

import org.identityconnectors.framework.common.objects.SchemaBuilder;

import

org.identityconnectors.framework.common.objects.filter.AbstractFilterTransla

tor;

import

org.identityconnectors.framework.impl.api.local.operations.FilteredResultsHa

ndler;

import org.identityconnectors.framework.spi.ConnectorClass;

import org.identityconnectors.framework.spi.operations.CreateOp;

import org.identityconnectors.framework.spi.operations.DeleteOp;

import org.identityconnectors.framework.spi.operations.SchemaOp;

import org.identityconnectors.framework.spi.operations.ScriptOnResourceOp;

import org.identityconnectors.framework.spi.operations.SearchOp;

import org.identityconnectors.framework.spi.operations.TestOp;

import org.identityconnectors.framework.spi.operations.UpdateOp;

@ConnectorClass(configurationClass=FlatFileConnectorConfiguration.class,

displayNameKey="connector.display")

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

public class FlatFileConnector implements

PoolableConnector,CreateOp,UpdateOp,DeleteOp,SearchOp<Map<String,String>>,Sc

hemaOp,TestOp {

private static final Log LOG = Log.getLog(FlatFileConnector.class);

private FlatFileConnectorConfiguration config;

private boolean isAlive;

private SshSession session;

public void init(Configuration cfg) {

//get the config object

this.config = (FlatFileConnectorConfiguration)cfg;

//this creates a connection to the target system

createSshConnection();

//connection is now alive

this.isAlive = true;

System.out.println("Finished init..!");

}

private void createSshConnection(){

SshSession returnSession = null;

FlatFileConnector ffc = this;

this.config.getPassword().access(new GuardedString.Accessor() {

@Override

public void access(char[] arg0) {

try {

session = new

SshSession(config.getHostName(),config.getUserName(),new String(arg0));

} catch (SshException e) {

throw new RuntimeException(e);

}

}

});

System.out.println("Successfully connected to the target system

"+this.config.getHostName()+" with user name "+this.config.getUserName());

}

/**

* {@inheritDoc}

*/

public void dispose() {

//disconnect the connection

this.session.disconnect();

//connection is no more alive

this.isAlive = false;

System.out.println("Finished dispose..!");

}

/**

* {@inheritDoc}

*/

public void checkAlive() {

if(!isAlive){

throw new IllegalStateException("This connector is dead!!");

}

}

/**

* {@inheritDoc}

*/

public Configuration getConfiguration() {

return this.config;

}

/**

* {@inheritDoc}

*/

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

public Uid create(ObjectClass oclass, Set<Attribute> attrs,

OperationOptions options) {

System.out.println(" #### Inside Create Method #### ");

Uid returnUid = null;

File targetFile = this.config.getTargetFile();

System.out.println(" #### Flat file is "+targetFile.getName());

String uidAttribute = this.config.getUniqueAttribute();

System.out.println(" ### UID Attribute is "+ uidAttribute);

File customScriptFile =

this.config.getCustomScriptForProvisioning();

// if customScriptFile is not null, means the user provided a custom

script when the IT resource was created in OIM advanced console,

// use this for provisioning.

if(customScriptFile != null){

String id=""; String lastName=""; String firstName=""; String

email="";

for(Attribute attr:attrs){

if(attr.getName().equalsIgnoreCase("AccountId"))

id = attr.getValue().get(0).toString();

if(attr.getName().equalsIgnoreCase("lastName"))

lastName = attr.getValue().get(0).toString();

if(attr.getName().equalsIgnoreCase("firstName"))

firstName = attr.getValue().get(0).toString();

if(attr.getName().equalsIgnoreCase("email"))

email = attr.getValue().get(0).toString();

}

ProcessBuilder pb = new

ProcessBuilder(customScriptFile.getAbsolutePath(),id,firstName,lastName,emai

l);

BufferedReader bufferedReader = null;

try {

pb = pb.redirectErrorStream(true);

Process process = pb.start();

InputStream inputStream = process.getInputStream();

InputStreamReader inputStreamReader = new

InputStreamReader(inputStream);

bufferedReader = new BufferedReader(inputStreamReader);

String line = null;

StringBuilder sb = new StringBuilder();

while ((line = bufferedReader.readLine()) != null) {

sb.append(line);

sb.append("\n");

}

if(sb.length() > 0){

String response = sb.toString();

if (response.contains(FlatFileUtil.ERROR_MESSAGE)){

throw new RuntimeException("Error while provisioning

"+sb);

}

}

} catch (IOException e) {

throw new RuntimeException("Error while executing script

"+customScriptFile+". Error is "+e.getMessage());

}

returnUid = FlatFileUtil.createUid(attrs, uidAttribute);

}else{

if((!FlatFileUtil.accountExists(attrs, targetFile))){

returnUid =

FlatFileUtil.createAccount(attrs,targetFile,uidAttribute);

}

}return returnUid;

}

/**

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

* {@inheritDoc}

*/

public Uid update(ObjectClass oclass, Uid uid, Set<Attribute>

replaceAttributes,

OperationOptions options) {

FlatFileConnectorConfiguration config =

(FlatFileConnectorConfiguration)this.getConfiguration();

File targetFile = config.getTargetFile();

StringBuffer updatedContent =

FlatFileUtil.checkAndGetUpdateAttributes(uid, replaceAttributes,

targetFile);

FlatFileUtil.writeUpdatedContent(updatedContent, targetFile);

return new Uid(uid.getUidValue());

}

/**

* {@inheritDoc}

*/

public void delete(ObjectClass oclass, Uid uid, OperationOptions

options) {

FlatFileConnectorConfiguration config =

(FlatFileConnectorConfiguration)this.getConfiguration();

File targetFile = config.getTargetFile();

StringBuffer updatedContent =

FlatFileUtil.deleteAttribute(uid.getUidValue(), targetFile);

FlatFileUtil.writeUpdatedContent(updatedContent,

targetFile);

}

@Override

public FilterTranslator<Map<String,String>>

createFilterTranslator(ObjectClass objectClass,OperationOptions

operationOptions) {

System.out.println(" ### Inside CreateFiterTranslator ####");

return new FlatFileFilterTranslator() {};

}

public void executeQuery(ObjectClass objectClass, Map<String,String>

query,

ResultsHandler resultsHandler,

OperationOptions operationOptions) {

System.out.println(" #### Inside Execute Query #### ");

System.out.println(" #### Query Map is "+query+" #### ");

FlatFileConnectorConfiguration config =

(FlatFileConnectorConfiguration)this.getConfiguration();

File targetFile = config.getTargetFile();

File lookupReconFirl = config.getLookupReconFile();

System.out.println(targetFile);

System.out.println(lookupReconFirl);

File customScriptFile = config.getCustomScriptForRecon();

String uniqueAttribute = config.getUniqueAttribute();

BufferedReader reader = FlatFileUtil.getReader(targetFile);

//In ICF we perform search operation for any type of recon... be it

lookup recon, normal recon

//operationOptions will help us to find out the same

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

//first get the attributes... attributes will have clue

String [] options = operationOptions.getAttributesToGet();

List<String> optionsList = Arrays.asList(options);

System.out.println("optionsList = "+optionsList);

String reconType = FlatFileUtil.Normal_RECON;

if(optionsList.contains(FlatFileUtil.ROLES)){

reconType = FlatFileUtil.LOOKUP_RECON;

}

// full recon but with script

if(reconType.equalsIgnoreCase(FlatFileUtil.Normal_RECON) &&

customScriptFile != null){

System.out.println("****Searching using custom script****");

ProcessBuilder pb = new

ProcessBuilder(customScriptFile.getAbsolutePath());

Process process;

try {

process = pb.start();

InputStream is = process.getInputStream();

InputStreamReader isReader = new InputStreamReader(is);

BufferedReader br = new BufferedReader(isReader);

String line = null;

StringBuilder sb = new StringBuilder();

while((line = br.readLine())!=null){

String s = line;

Scanner sc = new Scanner(s).useDelimiter(";");

while(sc.hasNext()){

String token = sc.next();

if(token.startsWith("lastName")){

ConnectorObjectBuilder cObjBuilder = new

ConnectorObjectBuilder();

String[] tokenArray = token.split(":");

cObjBuilder.addAttribute("lastName",tokenArray[1]);

cObjBuilder.setUid("lastName");

cObjBuilder.setName("lastName");

ConnectorObject cb = cObjBuilder.build();

resultsHandler.handle(cb);

}

}

}

} catch (IOException e) {

System.out.println("Exception..!! "+e.getMessage());

}

}

//normal recon but without scripts!

// here I need to check for operation option 'LatestToken', if this

is present and its value not null

//then need to get data from target based on 'LatestToken' in other

words incremental recon

else if(reconType.equalsIgnoreCase(FlatFileUtil.Normal_RECON) &&

customScriptFile == null){

Map<String,HashMap<String,List<String>>> searchResult = null;

if(optionsList.contains(FlatFileUtil.LatestToken)){

System.out.println("Performing incremental recon");

searchResult = getIncrementalSearchResult(reader,

targetFile, uniqueAttribute,query);

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

}else{

System.out.println("Performing full recon");

searchResult = getSearchResult(reader, targetFile,

uniqueAttribute,query);

}

Set<String> accountIds = searchResult.keySet();

for(String accountID:accountIds){

ConnectorObjectBuilder cObjBuilder = new

ConnectorObjectBuilder();

cObjBuilder.addAttribute(uniqueAttribute,accountID);

//cObjBuilder.setUid(uniqueAttribute);

HashMap<String,List<String>> attrNameValues =

searchResult.get(accountID);

Set<String> attrNames = attrNameValues.keySet();

for(String attrName:attrNames){

System.out.println("Adding attr "+attrName+" with value

"+attrNameValues.get(attrName)+" for accountID "+accountID);

cObjBuilder.addAttribute(attrName,attrNameValues.get(attrName));

}

cObjBuilder.addAttribute(FlatFileUtil.Incremental_Recon_Attribute, new

Date().getTime());

cObjBuilder.addAttribute(FlatFileUtil.LatestToken, new

Date().getTime());

Uid uid = new Uid(accountID);

cObjBuilder.setUid(uid);

cObjBuilder.setName(uid.getUidValue());

ConnectorObject cb = cObjBuilder.build();

resultsHandler.handle(cb);

}

}

else if(reconType.equalsIgnoreCase(FlatFileUtil.LOOKUP_RECON)){

//here is the place where we need write code which OIM is

expecting for look up recon

//we can have many lookup recons running, get which lookup recon

we need to run

//this is given in the scheduled tasks in OIM as 'Decode

Attribute'

System.out.println(config.getLookupReconFile());

File lookupReconFile = config.getLookupReconFile();

FileReader fReader = null;

BufferedReader bReader = null;

try {

fReader = new FileReader(lookupReconFile);

bReader = new BufferedReader(fReader);

String line = null;

while((line = bReader.readLine()) != null){

ConnectorObjectBuilder cObjBuilder = new

ConnectorObjectBuilder();

cObjBuilder.addAttribute(FlatFileUtil.ROLES,line);

cObjBuilder.setUid(FlatFileUtil.ROLES);

cObjBuilder.setName(FlatFileUtil.ROLES);

ConnectorObject cb = cObjBuilder.build();

resultsHandler.handle(cb);

}

} catch (FileNotFoundException e) {

System.out.println(e.getMessage());

throw new RuntimeException(e);

} catch (IOException e) {

System.out.println(e.getMessage());

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

throw new RuntimeException(e);

}

}

}

private Map<String,HashMap<String,List<String>>>

getIncrementalSearchResult(BufferedReader reader,File targetFile,String

uniqueAttribute,Map<String,String> queryMap){

return getSearchResult(reader, targetFile,

uniqueAttribute,queryMap);

}

private Map<String,HashMap<String,List<String>>>

getSearchResult(BufferedReader reader,File targetFile,String

uniqueAttribute,Map<String,String> queryMap){

List<String> result = new ArrayList<String>();

String searchAttribute=null;

String searchAttributeValue=null;

System.out.println(" Inside getSearchResult ");

// We want to check whether there is a filter attached for the recon

// if yes we only need to return the result that matches the filter

query

if(queryMap!=null){

searchAttribute=(String)queryMap.keySet().toArray()[0];

searchAttributeValue=queryMap.get(searchAttribute);

System.out.println(" **** Attribute "+searchAttribute +"

Value "+searchAttributeValue);

}

Map<String,HashMap<String,List<String>>> searchResult = new

HashMap<String, HashMap<String,List<String>>>();

String line = null;

try {

while((line = reader.readLine()) != null){

HashMap<String,List<String>> values = new

HashMap<String,List<String>>();

Scanner scanner = new Scanner(line).useDelimiter(";");

String uniqueAttrValue = null;

while(scanner.hasNext()){

List<String> valuesList = new ArrayList<String>();

String str = scanner.next();

String[] strSplit = str.split(":");

System.out.println(" Attribute "+strSplit[0]+ "

Value "+strSplit[1]);

if(strSplit[0].equalsIgnoreCase(uniqueAttribute)){

uniqueAttrValue = strSplit[1];

}else{

if(strSplit[0].equalsIgnoreCase(FlatFileUtil.Role)){

String[] valuesArray = strSplit[1].split(",");

valuesList.addAll(Arrays.asList(valuesArray));

values.put(FlatFileUtil.Role, valuesList);

}else

if(strSplit[0].equalsIgnoreCase(FlatFileUtil.email)){

valuesList.add(strSplit[1]);

values.put(FlatFileUtil.email, valuesList);

}else

if(strSplit[0].equalsIgnoreCase(FlatFileUtil.lastName)){

valuesList.add(strSplit[1]);

values.put(FlatFileUtil.lastName, valuesList);

}else

if(strSplit[0].equalsIgnoreCase(FlatFileUtil.FirstName)){

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

valuesList.add(strSplit[1]);

values.put(FlatFileUtil.FirstName, valuesList);

}else

if(strSplit[0].equalsIgnoreCase(FlatFileUtil.Salutation)){

valuesList.add(strSplit[1]);

values.put(FlatFileUtil.Salutation, valuesList);

}

}

}

// Check whether there is a search filter, if there is

filter add only those rows that match the search query

if(searchAttribute!=null && searchAttributeValue !=null){

boolean valueFound=false;

ArrayList<String> attributeValues = (ArrayList<String>)

values.get(searchAttribute);

for(String value:attributeValues){

System.out.println(" Searching through

"+searchAttribute +" Current vaue is "+value);

if(value.startsWith(searchAttributeValue)){

valueFound=true;

System.out.println(" Value "+ value +" Starts

with "+searchAttributeValue +" Need to add to Recon Result");

break;

}

}

if(valueFound){

searchResult.put(uniqueAttrValue, values);

}

}else if(searchAttribute ==null && searchAttributeValue

==null){

searchResult.put(uniqueAttrValue, values);

}

}

} catch (IOException e) {

throw new RuntimeException("Exception while reading the target

file "+e.getMessage());

}

System.out.println("Account Ids "+searchResult.keySet());

return searchResult;

}

public Schema schema() {

Set<AttributeInfo> attrInfoSet = new HashSet<AttributeInfo>();

attrInfoSet.add(AttributeInfoBuilder.build(FlatFileUtil.AccountId,

String.class));

attrInfoSet.add(AttributeInfoBuilder.build(FlatFileUtil.FirstName,

String.class));

attrInfoSet.add(AttributeInfoBuilder.build(FlatFileUtil.lastName,

String.class));

attrInfoSet.add(AttributeInfoBuilder.build(FlatFileUtil.email,

String.class));

attrInfoSet.add(AttributeInfoBuilder.build(FlatFileUtil.Role,

String.class));

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

attrInfoSet.add(AttributeInfoBuilder.build(FlatFileUtil.Salutation,

String.class));

attrInfoSet.add(AttributeInfoBuilder.build(FlatFileUtil.Password,

String.class));

attrInfoSet.add(AttributeInfoBuilder.build(FlatFileUtil.Incremental_Recon_At

tribute, String.class));

SchemaBuilder schemaBld = new

SchemaBuilder(FlatFileConnector.class);

schemaBld.defineObjectClass(ObjectClass.ACCOUNT_NAME, attrInfoSet);

Schema schema = schemaBld.build();

return schema;

}

public void test() {

createSshConnection();

}

}

Create a Java Class FlatFileFilterTranslator in package org.identityconnectors.flatfileconnector This code will allow us to run the reconciliation with a Filter to filter the results

Copy and paste the following java code: package org.identityconnectors.flatfileconnector;

import java.util.HashMap;

import java.util.Map;

import

org.identityconnectors.framework.common.objects.filter.AbstractFilterTranslator;

import org.identityconnectors.framework.common.objects.filter.StartsWithFilter;

import org.identityconnectors.framework.common.objects.Attribute;

public class FlatFileFilterTranslator extends

AbstractFilterTranslator<Map<String,String>> {

@Override

protected Map<String,String> createStartsWithExpression(StartsWithFilter

startFilter,boolean not){

Map<String,String> startsMap = new HashMap<String,String>();

System.out.println("#### Inside FlatFileFilterTranslator #### ");

String queryString= new String();

Attribute attr=startFilter.getAttribute();

System.out.println(" ## Attribute Name "+attr.getName() +" Value

"+attr.getValue().get(0).toString());

startsMap.put(attr.getName(), attr.getValue().get(0).toString());

return startsMap;

}

} Click on Save

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Create a Java Class FlatFileUtil in package org.identityconnectors.flatfileconnector.util (notice the .util at the end of the package name).

Copy and paste the following java code: package org.identityconnectors.flatfileconnector.util;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

import java.io.Reader;

import java.io.Writer;

import java.util.HashSet;

import java.util.List;

import java.util.Scanner;

import java.util.Set;

import org.identityconnectors.framework.common.exceptions.ConnectorIOException;

import org.identityconnectors.framework.common.objects.Attribute;

import org.identityconnectors.framework.common.objects.AttributeUtil;

import org.identityconnectors.framework.common.objects.ConnectorObject;

import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;

import org.identityconnectors.framework.common.objects.Uid;

/**

* The class FlatFileUtil contains utility methods such as createUid etc.

*

* @author manju

*/

public final class FlatFileUtil {

public static String ERROR_MESSAGE = "account already exists";

public static String LOOKUP_RECON_STRING = "Lookup.FF.UM.LookupRecon";

public static String LOOKUP_RECON = "LOOKUP_RECON";

public static String Normal_RECON = "Normal_RECON";

public static String Incremental_RECON = "Incremental_RECON";

public static String LatestToken="LatestToken";

public static String ROLES = "Roles";

public static String salutation = "salutation";

public static String LOOKUP_RECON_FILE = "/home/mambuga/misc/roles.txt";

public static String AccountId="AccountId";

public static String FirstName="FirstName";

public static String lastName="lastName";

public static String email="email";

public static String Role="Role";

public static String Salutation="Salutation";

public static String Incremental_Recon_Attribute="LastModified";

public static String Password="Password";

/**

* Creates and retunrs a Uid with the input value

* @param value

* @return Uid

*/

public static Uid getUid(String value){

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Uid uid = new Uid(value);

return uid;

}

/**

* Returns a writer object for the input string

* @param file name

* @return buffered writer

*/

public static BufferedWriter getWriter(File file,boolean append){

BufferedWriter writer = null;

try {

writer = new BufferedWriter(new FileWriter(file,append));

} catch (IOException e) {

e.printStackTrace();

throw new RuntimeException("Unable to get a writer!");

}

return writer;

}

/**

* Returns a reader object

* @param file

* @return

*/

public static BufferedReader getReader(File file){

BufferedReader reader = null;

try {

reader = new BufferedReader(new FileReader(file));

} catch (FileNotFoundException e) {

e.printStackTrace();

throw new RuntimeException("Unable to get a reader!");

}

return reader;

}

/**

* Closes the reader

* @param reader

*/

public static void closeReader(Reader reader){

try {

reader.close();

} catch (IOException e) {

e.printStackTrace();

throw new RuntimeException("Unable to close the reader

"+e.getMessage());

}

}

/**

* Closes the writer

* @param writer

*/

public static void closeWriter(Writer writer){

try {

writer.close();

} catch (IOException e) {

e.printStackTrace();

throw new RuntimeException("Unable to close the writer

"+e.getMessage());

}

}

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

/**

* Creates a user on the target system, creates a corresponding Uid and returns

it

* @param arg1

* @param targetFile

* @return

*/

public static Uid createAccount(Set<Attribute> arg1,File targetFile,String

uidAttribute){

System.out.println(" ### Inside FlatFileUtil -> Create Account ###");

Uid returnUid = null;

BufferedWriter writer = FlatFileUtil.getWriter(targetFile,true);

try{

for(Attribute thisAttribute:arg1){

System.out.println(" ### Attribute Name

"+thisAttribute.getName() +" Value "+thisAttribute.getValue()+" ####");

writer.write(thisAttribute.getName());

writer.write(":");

List<Object> valueList = thisAttribute.getValue();

for(Object thisValue:valueList){

writer.write(thisValue.toString());

writer.write(";");

}

}

writer.write("\n");

writer.flush();

}catch(IOException e){

throw new ConnectorIOException("IOException while creating

"+e.getMessage());

}

Attribute thisAttribute = AttributeUtil.find(uidAttribute, arg1);

System.out.println(" #### Found UID Attribute

"+thisAttribute.getName()+ " "+thisAttribute.getValue()+" #### ");

String uidValue = thisAttribute.getValue().get(0).toString();

returnUid = FlatFileUtil.getUid(uidValue);

FlatFileUtil.closeWriter(writer);

return returnUid;

}

private static String getAttributeValue(Set<Attribute> attributes, String

name){

String value = null;

for(Attribute attr:attributes){

if(attr.getName().equalsIgnoreCase(name)){

value = attr.getValue().get(0).toString();

}

}

return value;

}

/**

* Checks if account to be created on target system already exists

* @param arg1

* @param file

* @return

*/

public static boolean accountExists(Set<Attribute> arg1,File file){

boolean accountExist = false;

BufferedReader reader = FlatFileUtil.getReader(file);

String s = null;

String existingAccountID = getAttributeValue(arg1, "AccountId");

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

try {

while((s = reader.readLine()) != null){

Scanner sc = new Scanner(s).useDelimiter(",");

System.out.println("s = "+s);

while(sc.hasNext()){

String nextStr = sc.next();

System.out.println("nextStr = "+nextStr);

String[] strArray = nextStr.split(":");

System.out.println("strArray.length =

"+strArray.length);

for(String str:strArray){

System.out.println("inside for str =

"+str);

}

if(existingAccountID.equalsIgnoreCase(strArray[strArray.length-1])){

accountExist = true;

}

}

}

} catch (IOException e) {

e.printStackTrace();

throw new RuntimeException("Unable to read from file

"+file.getAbsolutePath());

}

FlatFileUtil.closeReader(reader);

return accountExist;

}

/**

* Creates the ConnectorObject

* @param line

* @return

*/

public static ConnectorObject createConnectorObject(String line){

ConnectorObject cb = null;

ConnectorObjectBuilder cBuilder = new ConnectorObjectBuilder();

Scanner sc = new Scanner(line).useDelimiter(",");

while(sc.hasNext()){

String nextStr = sc.next();

String[] strArray = nextStr.split(":");

cBuilder.addAttribute(strArray[0], strArray[1]);

if(strArray[0].equalsIgnoreCase("AccountId")){

cBuilder.setUid(strArray[0]);

cBuilder.setName(strArray[0]);

}

}

cb = cBuilder.build();

return cb;

}

public static StringBuffer checkAndGetUpdateAttributes(Uid arg1,Set<Attribute>

arg2,File targetFile){

//boolean updatedAttributes = false;

Set<Attribute> updatedAttributes= new HashSet();

System.out.println("Inside checkAndGetUpdateAttributes...");

System.out.println(" ### User ID is "+arg1.getName() +" Value is

"+arg1.getUidValue());

for(Attribute attr:arg2){

System.out.println(" ### Changed Attribute is "+attr.getName() +"

Changed Value is "+attr.getValue());

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

// We wan't to discard the __CURRENT_PASSWORD__ attributes, since

we don't want this in the flat file

if(!attr.getName().equalsIgnoreCase("__CURRENT_PASSWORD__")){

updatedAttributes.add(attr);

}

}

boolean attributeFound = false;

BufferedReader reader = FlatFileUtil.getReader(targetFile);

String line = null;

StringBuffer sb = new StringBuffer();

try {

while((line = reader.readLine()) != null){

attributeFound = false;

Scanner sc = new Scanner(line).useDelimiter(";");

while(sc.hasNext()){

String nextString = sc.next();

String[] strArray = nextString.split(":");

if(strArray[0].equals("AccountId") &&

strArray[1].equalsIgnoreCase(arg1.getUidValue())){

System.out.println(" ## Attribute

Found ##");

attributeFound = true;

break;

}

}

if(!attributeFound){

sb.append(line);

sb.append("\n");

}else{

StringBuffer updatedLine = new StringBuffer();

for(Attribute attr:updatedAttributes){

System.out.println("line = "+line);

System.out.println("Attribute Name :

"+attr.getName()+" Value : " +attr.getValue());

//we will enter here if line does not

contains the replace attr value and name

String[] strArray = null;

//if(line.contains(attr.getName())){

strArray = line.split(attr.getName());

//}

if(attr.getName().equalsIgnoreCase(FlatFileUtil.Role))

{

System.out.println("Updating

role...");

if(strArray[0] != null)

line = strArray[0];

updatedLine.append(attr.getName());

updatedLine.append(":");

for(int i

=0;i<attr.getValue().size();i++){

updatedLine.append(attr.getValue().get(i));

updatedLine.append(",");

}

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

}else{

System.out.println("Updating

"+attr.getName());

if(strArray.length > 1 && strArray[1]

!= null){

line = "";

updatedLine.append(strArray[0]);

updatedLine.append(attr.getName());

updatedLine.append(":");

updatedLine.append(attr.getValue().get(0));

updatedLine.append(";");

Scanner sc1 = new

Scanner(strArray[1]).useDelimiter(";");

int i = 0;

while(sc1.hasNext()){

if(i == 0){

sc1.next();

i++;

}else{

updatedLine.append(sc1.next());

updatedLine.append(";");

}

}

//updatedLine.append(strArray[1]);

}else{

System.out.println("Updating to

value "+attr.getValue().get(0));

updatedLine.append(attr.getName());

updatedLine.append(":");

updatedLine.append(attr.getValue().get(0));

updatedLine.append(";");

}

}

//

if(!line.contains(attr.getValue().toString()) &&

!line.contains(attr.getName().toString())){

//

updatedLine.append(attr.getName()).append(":").append(attr.getValue().get(attr.getValu

e().size()-1)).append(",");

// }//we come here if we have the attr name

already in the line

// else

if(!line.contains(attr.getValue().toString()) &&

line.contains(attr.getName().toString())){

//

updatedLine.append(attr.getValue().get(attr.getValue().size()-1)).append(",");

// }

}

System.out.println("updatedLine = "+updatedLine);

sb.append(line);

sb.append(updatedLine);

sb.append("\n");

}

}

} catch (IOException e) {

e.printStackTrace();

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

throw new RuntimeException("Unable to read from file

"+targetFile.getAbsolutePath());

}

System.out.println("Name: "+arg1.getName());

System.out.println("UId value: "+arg1.getUidValue());

System.out.println("value: "+arg1.getValue());

FlatFileUtil.closeReader(reader);

return sb;

}

public static void writeUpdatedContent(StringBuffer updatedContent,File file){

BufferedWriter writer = FlatFileUtil.getWriter(file,false);

try {

writer.write(updatedContent.toString());

writer.flush();

} catch (IOException e) {

e.printStackTrace();

throw new RuntimeException("Unable to write to file

"+file.getAbsolutePath());

}

FlatFileUtil.closeWriter(writer);

}

public static StringBuffer deleteAttribute(String uidValue,File file){

boolean accountExists = false;

BufferedReader reader = FlatFileUtil.getReader(file);

String line = null;

StringBuffer newContent = new StringBuffer();

try {

while((line = reader.readLine()) != null){

accountExists = false;

Scanner sc = new Scanner(line).useDelimiter(";");

while(sc.hasNext()){

String[] strArray = sc.next().split(":");

if(strArray[strArray.length-

1].equalsIgnoreCase(uidValue)){

accountExists = true;

break;

}

}

if(!accountExists){

newContent.append(line);

newContent.append("\n");

}

}

} catch (IOException e) {

e.printStackTrace();

throw new RuntimeException("Cannot read from file

"+file.getAbsolutePath());

}

FlatFileUtil.closeReader(reader);

return newContent;

}

public static Uid createUid(Set<Attribute> arg1,String uidAttribute){

Uid returnValue = null;

Attribute thisAttribute = AttributeUtil.find(uidAttribute, arg1);

String uidValue = thisAttribute.getValue().get(0).toString();

returnValue = FlatFileUtil.getUid(uidValue);

return returnValue;

}

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

}

Click on Save The JDeveloper screen should look like this:

4.3 Test for successful connection with target

Have a look to the Java Class FlatFileConnector and notice the implementation of the org.identityconnectors.framework.spi.operations.TestOp interface and that the public void test() implementation call the createSshConnection API as shown below.

public void test() { createSshConnection();

}

Creating the flat file connector bundle:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Right click on FlatFileConnectorProject, click New, select Ant and “Buildfile from Project“ and click OK.

Click OK again on the next screen

Right click on FlatFileConnectorProject and select “Project Properties“ and under Ant, select Properties and add the following

Property Value

MAJOR 1

MINOR 0

ConnectorBundle-FrameworkVersion 1.0

ConnectorBundle-Name org.identityconnectors.flatfile

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Click OK

Also update the build.xml to add a new task:

<target name="buildJar" depends="compile " description="Create binary

distribution">

<manifest file="MANIFEST.MF">

<attribute name="ConnectorBundle-FrameworkVersion"

value="${ConnectorBundle-FrameworkVersion}"/>

<attribute name="ConnectorBundle-Name" value="${ConnectorBundle-Name}"/>

<attribute name="ConnectorBundle-Version" value="${MAJOR}.${MINOR}"/>

</manifest> <jar jarfile="${basedir}/${ConnectorBundle-Name}-

${MAJOR}.${MINOR}.jar" basedir="${output.dir}" manifest="MANIFEST.MF"/>

</target>

Update the all target adding buildJar:

<target name="all" description="Build the project"

depends="compile,copy,buildJar"/>

The result should look like:

Click on “Save All”

The directory structure after creation looks like below:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Build, Compile and create the jar:

Right click on build.xml, select “Run Ant Target” -> all This creates flat file connector jar (org.identityconnectors.flatfile-1.0.jar) in /app/home/oracle/jdeveloper/mywork/FlatFileConnectorApplication/FlatFileConnectorProject

Copy this jar file to the connector server bundle directory (/app/Connector_Server_111200/connector_server_java-1.4.0/bundles) and re-start the Connector Server (Ctrl-C in the Connector Server terminal to stop it).

Create standalone java client: Right-click on FlatFileConnectorTestProject, click on New... and select select Java Class.

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Name it Test using package org.identityconnectors.flatfileconnector.test.operation Copy and paste the following java code:

package org.identityconnectors.flatfileconnector.test.operation;

import java.io.File;

import java.util.HashSet;

import java.util.Set;

import org.identityconnectors.common.security.GuardedString;

import

org.identityconnectors.flatfileconnector.test.common.BundleProperties;

import org.identityconnectors.framework.api.APIConfiguration;

import org.identityconnectors.framework.api.ConfigurationProperties;

import org.identityconnectors.framework.api.ConnectorInfoManager;

import org.identityconnectors.framework.api.ConnectorInfoManagerFactory;

import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo;

import org.identityconnectors.framework.common.objects.Attribute;

import org.identityconnectors.framework.common.objects.AttributeBuilder;

public abstract class Test {

ConnectorInfoManager cInfoManager = null;

public Test(){

this.setupConnectorServer();

}

public void setupConnectorServer(){

ConnectorInfoManagerFactory cInfoManagerFactory =

ConnectorInfoManagerFactory.getInstance();

//Connector server information

String connectorServerhost = "localhost";

int connectorServerPort = 8759;

GuardedString connectorServerKey = new

GuardedString("12345".toCharArray());

RemoteFrameworkConnectionInfo remoteConnectionInfo = new

RemoteFrameworkConnectionInfo(connectorServerhost, connectorServerPort,

connectorServerKey, false, null, 0);

cInfoManager =

cInfoManagerFactory.getRemoteManager(remoteConnectionInfo);

if (cInfoManager == null) {

System.out.println("this should never happen.!!");

return;

}

}

/* public void setupConnectorServer(){

BundleProperties bProps = new BundleProperties();

ConnectorInfoManagerFactory cInfoManagerFactory =

ConnectorInfoManagerFactory.getInstance();

ConnectorInfoManager cInfoManager =

cInfoManagerFactory.getLocalManager(bProps.getUrl());

} */

public ConnectorInfoManager getCInfoManager() {

return this.cInfoManager;

}

public void setUpConfigurationProperties(ConfigurationProperties

configProps){

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

//now set values for configProps on SPI side

configProps.setPropertyValue("targetFile", new

File("/app/connector_files/output.txt"));

configProps.setPropertyValue("uniqueAttribute", "AccountId");

configProps.setPropertyValue("hostName", "identity");

configProps.setPropertyValue("userName", "oracle");

configProps.setPropertyValue("password", new

GuardedString("Oracle123".toCharArray()));

configProps.setPropertyValue("lookupReconFile", new

File("/app/connector_files/roles.txt"));

}

public void setPoolConfigurations(APIConfiguration apiConfig){

apiConfig.getConnectorPoolConfiguration().setMaxIdle(10);

apiConfig.getConnectorPoolConfiguration().setMaxObjects(10);

apiConfig.getConnectorPoolConfiguration().setMaxWait(150*1000);

apiConfig.getConnectorPoolConfiguration().setMinEvictableIdleTimeMillis(120*

1000);

apiConfig.getConnectorPoolConfiguration().setMinIdle(1);

}

public Set<Attribute> updateAttribute(String accountID,String

updateField){

Set<Attribute> attributes = new HashSet<Attribute>();

//Attribute id = AttributeBuilder.build("AccountId", accountID);

Attribute updatedAttr = AttributeBuilder.build(updateField,

updateField+accountID);

//attributes.add(id);

attributes.add(updatedAttr);

return attributes;

}

public Set<Attribute> enable(String updateField,String flag){

Set<Attribute> attributes = new HashSet<Attribute>();

//Attribute id = AttributeBuilder.build("AccountId", accountID);

Attribute updatedAttr = AttributeBuilder.build(updateField,

flag);

//attributes.add(id);

attributes.add(updatedAttr);

return attributes;

}

//utility method to create the attributes.. these will be provided from

OIM

public Set<Attribute> createAttributes(String accountID){

Set<Attribute> attributes = new HashSet<Attribute>();

Attribute id = AttributeBuilder.build("AccountId",

accountID);

Attribute firstName =

AttributeBuilder.build("FirstName", "Shekar");

Attribute lastName = AttributeBuilder.build("lastName",

"Nagaraja"+accountID);

Attribute email = AttributeBuilder.build("email",

"[email protected]");

attributes.add(id);

attributes.add(firstName);

attributes.add(lastName);

attributes.add(email);

return attributes;

}

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

}

Create a folder connector_files in /app and create an empty output.txt file there.

Create standalone java client TestConnection in package org.identityconnectors.flatfileconnector.test.operation in FlatFileConnectorTestProject. Copy and paste this java code:

package org.identityconnectors.flatfileconnector.test.operation;

import java.io.File;

import java.net.URL;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

import org.identityconnectors.common.security.GuardedString;

import

org.identityconnectors.flatfileconnector.test.common.BundleProperties;

import org.identityconnectors.framework.api.APIConfiguration;

import org.identityconnectors.framework.api.ConfigurationProperties;

import org.identityconnectors.framework.api.ConnectorFacade;

import org.identityconnectors.framework.api.ConnectorFacadeFactory;

import org.identityconnectors.framework.api.ConnectorInfo;

import org.identityconnectors.framework.api.ConnectorInfoManager;

import org.identityconnectors.framework.api.ConnectorInfoManagerFactory;

import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo;

import org.identityconnectors.framework.api.operations.APIOperation;

import org.identityconnectors.framework.common.objects.Attribute;

import org.identityconnectors.framework.common.objects.AttributeBuilder;

import org.identityconnectors.framework.common.objects.ObjectClass;

import org.identityconnectors.framework.common.objects.OperationOptions;

import

org.identityconnectors.framework.common.objects.OperationOptionsBuilder;

import org.identityconnectors.framework.common.objects.ResultsHandler;

import org.identityconnectors.framework.common.objects.Uid;

import org.identityconnectors.framework.common.objects.filter.AndFilter;

import

org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilt

er;

import org.identityconnectors.framework.common.objects.filter.Filter;

import org.identityconnectors.framework.common.objects.filter.NotFilter;

public class TestConnection extends Test{

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

TestConnection(){

super();

}

private void create(){

//get a list of ConnectorInfo from ConnectorInfoManager

List<ConnectorInfo> cInfos =

this.getCInfoManager().getConnectorInfos();

//iterate through the list

for(ConnectorInfo cInfo:cInfos){

//get the APIconfig

APIConfiguration apiConfig =

cInfo.createDefaultAPIConfiguration();

//set pool configurations

setPoolConfigurations(apiConfig);

//get the configProps i.e reference to Configuration on SPI side

ConfigurationProperties configProps =

apiConfig.getConfigurationProperties();

//set up config props

this.setUpConfigurationProperties(configProps);

//get a reference to ConnectorFacadeFactory

ConnectorFacadeFactory facadeFactory =

ConnectorFacadeFactory.getInstance();

//create the connector facade (nothing but reference to our

Connector on SPI side)

ConnectorFacade connectorFacade =

facadeFactory.newInstance(apiConfig);

//lets call the create operation on the facade

connectorFacade.test();

}

}

public static void main(String[] args){

new TestConnection().create();

}

}

Here we would be calling the test API on the connector façade instance which in turn would call the test API on the connector implementation:

Open org.identityconnectors.flatfileconnector.test.operation.Test and double-check connectorServerhost, connectorServerPort, connectorServerKey to appropriate values as provided by you while setting up the connector server.

Update the CLASSPATH for this project to include the newly created flat file bundle JAR (Project Properties -> Add JAR / Directory → org.identityconnectors.flatfile-1.0.jar in /app/home/oracle/jdeveloper/mywork/FlatFileConnectorApplication/FlatFileConnectorProject) and the other jars like you did in the other project:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Create a Java Class BundleProperties in package org.identityconnectors.flatfileconnector.test.common Copy and paste the following java code:

package org.identityconnectors.flatfileconnector.test.common;

import java.io.File;

import java.io.IOException;

import java.net.URL;

import org.identityconnectors.common.IOUtil;

import

org.identityconnectors.framework.common.exceptions.ConnectorException;

public class BundleProperties {

final String dirLocation =

"/app/Connector_Server_111200/connector_server_java-1.4.0/bundles";

final String jarName = "org.identityconnectors.flatfile-1.0";

final String jarRelativeUrl = jarName + ".jar";

final String connectorClassName =

"org.identityconnectors.flatfileconnector.FlatFileConnector";

File dir = new File(dirLocation);

URL url;

public BundleProperties() {

try {

url = IOUtil.makeURL(dir, jarRelativeUrl);

} catch (final IOException ioe) {

System.out.println("URL not framed properly");

ioe.printStackTrace();

throw ConnectorException.wrap(ioe);

}

}

public String getDirLocation() {

return dirLocation;

}

public String getJarName() {

return jarName;

}

public String getJarRelativeUrl() {

return jarRelativeUrl;

}

public String getConnectorClassName() {

return connectorClassName;

}

public File getDir() {

return dir;

}

public URL getUrl() {

return url;

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

}

}

Verifying if connector can make a connection to the target: Notice the Connector Server configuration that has been defined in Test.java: Note: If needed change the connectorServerhost below to refeclt your

host name on which connector server is running.

public void setupConnectorServer(){

ConnectorInfoManagerFactory cInfoManagerFactory =

ConnectorInfoManagerFactory.getInstance();

//Connector server information

String connectorServerhost = "localhost";

int connectorServerPort = 8759;

GuardedString connectorServerKey = new

GuardedString("12345".toCharArray());

Right click on TestConnection.java, click Run and see below outputs in

JDeveloper console:

Connector Server terminal:

5. Provisioning

5.1 Introduction

Process that grants users, groups appropriate access rights. It involves creation of user (if not already in system), granting or revoking (deprovision) rights to access resource (application, system, database…)

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Provisioning involves create, update, delete, enable and disable a user on the target system. ICF provides various interfaces for the same. Connector SPI developer needs to find out what operations are supported by the target system and implement appropriate interfaces.

5.2 Provisioning Operations

5.2.1. Create

This operation creates an entity’s details (user or group) on the target system. ICF provides org.identityconnectors.framework.spi.operations.CreateOp interface to achieve the same. Connector bundles which needs to provide this functionality should implement this interface and provide implementation to the Uid create(ObjectClass oclass,java.util.Set<Attribute> attrs,OperationOptions options) API. The Connector API should create a set of Attributes containing all the form field names and values and should pass them onto this method. This method also takes an ObjectClass (either Account or Group) and OperationOptions. With the current ICF common glue for OIM (icf-oim-intg.jar), OperationOptions have not been used. The implementation of this method is responsible to take all the attributes passed and insert them in the target system. In FlatFileConnectorProject, open FlatFileConnector.java and take some time investigating the following implementation of the related method:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

5.2.2. Update This operation updates an entity’s (user or group) details on the target system. ICF provides org.identityconnectors.framework.spi.operations.UpdateOp interface to achieve the same. Connector bundles which needs to provide this functionality should implement this interface and provide implementation to the Uid update(ObjectClass objclass, Uid uid,java.util.Set<Attribute> replaceAttributes,OperationOptions options) API. The Connector API should create a set of Attributes containing all the form field names and updated values and should pass them onto this method. This method also takes a Uid representing the entity being updated, an ObjectClass (either Account or Group) and OperationOptions. With the current ICF common glue for OIM (icf-oim-intg.jar), OperationOptions have not been used. The implementation of this method is responsible to take all the attributes passed and replace them with existing attributes of the entity in the target system. Look after the details of this method:

5.2.3. Delete

This operation deletes an entity on the target system. ICF provides org.identityconnectors.framework.spi.operations.DeleteOp interface to achieve the same. Connector bundles which needs to provide this functionality should implement this interface and provide implementation to the void delete(ObjectClass objClass,Uid uid,OperationOptions options) API. The Connector API should send in the Uid representing the entity being deleted from target system. The implementation is responsible to accept the Uid passed in and delete the corresponding entity on the target system. Check also how it has been implemented:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

5.2.4 Enable This operation should enable an entity on the target system. We should make use of the Update interface discussed in the section 5.2.2. The connector API should pass all the attributes of the existing entity along with a new attribute __ENABLE_ with a value of true. The implementation is responsible to take this new attribute and perform target system specific operation such that the entity gets enabled on the target system. The implementation as shown in section 5.2.2 will also handle this operation. Look at the code for update in TestUpdate.java in section 5.2.6.2 section below. In that section we update the user with Enable attribute set to either true or false which will enable or disable the user. 5.2.5. Disable This operation should disable an entity on the target system. We should again make use of the Update interface discussed in the section 5.2.2. The connector API should pass all the attributes of the existing entity along with a new attribute __ENABLE__ with the value of false. The implementation is responsible to take this new attribute and perform target system specific operation such that the entity gets disabled on the target system. The implementation as shown in section 5.2.2 will also handle this operation as well. Look at the code for update in TestUpdate.java in section 5.2.6.2 section below. In that section we update the user with Enable attribute set to either true or false which will enable or disable the user.

5.2.6. Testing

There is no need to create the bundle jar, copy it in the connector server bundle directory and re-

start the connector server because we didn't change anything in there. In FlatFileConnectorTestProject, open

org.identityconnectors.flatfileconnector.test.operation.Test and check that the values are correct for ConfigurationProperties under the method setUpConfigurationProperties.

ConfigurationProperties are:

targetFile – file to be present on the target machine

uniqueAttribute – do not update this variable.

hostName – target machine name which has the flat file

userName – user name with which to connect to target machine

password – password of the above user

lookupReconFile – text files containing roles.

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

5.2.6.1 Testing create operation

Note: Before running the java class, be sure to have created an empty output.txt & roles.txt files

in the target folder (/app/connector_files).

In FlatFileConnectorTestProject, create the related standalone java client TestCreate in package org.identityconnectors.flatfileconnector.test.operation Use the following java code:

package org.identityconnectors.flatfileconnector.test.operation;

import java.io.File;

import java.net.URL;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

import org.identityconnectors.common.security.GuardedString;

import

org.identityconnectors.flatfileconnector.test.common.BundleProperties;

import org.identityconnectors.framework.api.APIConfiguration;

import org.identityconnectors.framework.api.ConfigurationProperties;

import org.identityconnectors.framework.api.ConnectorFacade;

import org.identityconnectors.framework.api.ConnectorFacadeFactory;

import org.identityconnectors.framework.api.ConnectorInfo;

import org.identityconnectors.framework.api.ConnectorInfoManager;

import org.identityconnectors.framework.api.ConnectorInfoManagerFactory;

import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo;

import org.identityconnectors.framework.api.operations.APIOperation;

import org.identityconnectors.framework.common.objects.Attribute;

import org.identityconnectors.framework.common.objects.AttributeBuilder;

import org.identityconnectors.framework.common.objects.ObjectClass;

import org.identityconnectors.framework.common.objects.OperationOptions;

import

org.identityconnectors.framework.common.objects.OperationOptionsBuilder;

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

import org.identityconnectors.framework.common.objects.ResultsHandler;

import org.identityconnectors.framework.common.objects.Uid;

import org.identityconnectors.framework.common.objects.filter.AndFilter;

import

org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilt

er;

import org.identityconnectors.framework.common.objects.filter.Filter;

import org.identityconnectors.framework.common.objects.filter.NotFilter;

public class TestCreate extends Test{

TestCreate(){

super();

}

private void create(){

//get a list of ConnectorInfo from ConnectorInfoManager

List<ConnectorInfo> cInfos =

this.getCInfoManager().getConnectorInfos();

//iterate through the list

for(ConnectorInfo cInfo:cInfos){

//get the APIconfig

APIConfiguration apiConfig =

cInfo.createDefaultAPIConfiguration();

//set pool configurations

setPoolConfigurations(apiConfig);

//get the configProps i.e reference to Configuration on SPI side

ConfigurationProperties configProps =

apiConfig.getConfigurationProperties();

//set up config props

this.setUpConfigurationProperties(configProps);

//get a reference to ConnectorFacadeFactory

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

ConnectorFacadeFactory facadeFactory =

ConnectorFacadeFactory.getInstance();

//create the connector facade (nothing but reference to our

Connector on SPI side)

ConnectorFacade connectorFacade =

facadeFactory.newInstance(apiConfig);

//lets call the create operation on the facade

connectorFacade.create(ObjectClass.ACCOUNT,

createAttributes("1"), null);

connectorFacade.create(ObjectClass.ACCOUNT,

createAttributes("2"), null);

connectorFacade.create(ObjectClass.ACCOUNT,

createAttributes("3"), null);

}

}

public static void main(String[] args){

new TestCreate().create();

}

}

Run the standalone java client org.identityconnectors.flatfileconnector.test.operation.TestCreate

Note: Before running the java class, be sure to have created an empty output.txt & roles.txt files in the target folder (/app/connector_files).

Verify to see correct user data in the output.txt flat file:

5.2.6.2 Testing update operation

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

In FlatFileConnectorTestProject project, create the related standalone java client TestUpdate in package org.identityconnectors.flatfileconnector.test.operation Use the following java code:

package org.identityconnectors.flatfileconnector.test.operation;

import java.io.File;

import java.net.URL;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

import org.identityconnectors.common.security.GuardedString;

import

org.identityconnectors.flatfileconnector.test.common.BundleProperties;

import org.identityconnectors.framework.api.APIConfiguration;

import org.identityconnectors.framework.api.ConfigurationProperties;

import org.identityconnectors.framework.api.ConnectorFacade;

import org.identityconnectors.framework.api.ConnectorFacadeFactory;

import org.identityconnectors.framework.api.ConnectorInfo;

import org.identityconnectors.framework.api.ConnectorInfoManager;

import org.identityconnectors.framework.api.ConnectorInfoManagerFactory;

import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo;

import org.identityconnectors.framework.api.operations.APIOperation;

import org.identityconnectors.framework.common.objects.Attribute;

import org.identityconnectors.framework.common.objects.AttributeBuilder;

import org.identityconnectors.framework.common.objects.ObjectClass;

import org.identityconnectors.framework.common.objects.OperationOptions;

import

org.identityconnectors.framework.common.objects.OperationOptionsBuilder;

import org.identityconnectors.framework.common.objects.ResultsHandler;

import org.identityconnectors.framework.common.objects.Uid;

import org.identityconnectors.framework.common.objects.filter.AndFilter;

import

org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilt

er;

import org.identityconnectors.framework.common.objects.filter.Filter;

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

import org.identityconnectors.framework.common.objects.filter.NotFilter;

public class TestUpdate extends Test {

public TestUpdate() {

super();

}

private void update(){

//get a list of ConnectorInfo from ConnectorInfoManager

List<ConnectorInfo> cInfos =

this.getCInfoManager().getConnectorInfos();

//iterate through the list

for(ConnectorInfo cInfo:cInfos){

//get the APIconfig

APIConfiguration apiConfig =

cInfo.createDefaultAPIConfiguration();

//set pool configurations

setPoolConfigurations(apiConfig);

//get the configProps i.e reference to Configuration on SPI

side

ConfigurationProperties configProps =

apiConfig.getConfigurationProperties();

//set up config props

this.setUpConfigurationProperties(configProps);

//get a reference to ConnectorFacadeFactory

ConnectorFacadeFactory facadeFactory =

ConnectorFacadeFactory.getInstance();

//create the connector facade (nothing but reference to our

Connector on SPI side)

ConnectorFacade connectorFacade =

facadeFactory.newInstance(apiConfig);

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

//lets call the update operation on the facade... i.e update

the user with account ID 1 to 10

connectorFacade.update(ObjectClass.ACCOUNT, new Uid("1"),

updateAttribute("10","FirstName"), null);

//lets enable account ID 1

connectorFacade.update(ObjectClass.ACCOUNT, new Uid("1"),

enable("Enable","true"), null);

//lets disable account ID 2

connectorFacade.update(ObjectClass.ACCOUNT, new Uid("2"),

enable("Enable","false"), null);

}

}

public static void main(String[] args){

new TestUpdate().update();

}

}

Run the stand alone java client

org.identityconnectors.flatfileconnector.test.operation.TestUpdate Verify to see correct user data in the output.txt flat file:

5.2.6.3 Testing the delete operation In FlatFileConnectorTestProject project, create the related standalone java client TestDelete in package org.identityconnectors.flatfileconnector.test.operation Use the following java code:

package org.identityconnectors.flatfileconnector.test.operation;

import java.io.File;

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

import java.net.URL;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

import org.identityconnectors.common.security.GuardedString;

import

org.identityconnectors.flatfileconnector.test.common.BundleProperties;

import org.identityconnectors.framework.api.APIConfiguration;

import org.identityconnectors.framework.api.ConfigurationProperties;

import org.identityconnectors.framework.api.ConnectorFacade;

import org.identityconnectors.framework.api.ConnectorFacadeFactory;

import org.identityconnectors.framework.api.ConnectorInfo;

import org.identityconnectors.framework.api.ConnectorInfoManager;

import org.identityconnectors.framework.api.ConnectorInfoManagerFactory;

import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo;

import org.identityconnectors.framework.api.operations.APIOperation;

import org.identityconnectors.framework.common.objects.Attribute;

import org.identityconnectors.framework.common.objects.AttributeBuilder;

import org.identityconnectors.framework.common.objects.ObjectClass;

import org.identityconnectors.framework.common.objects.OperationOptions;

import

org.identityconnectors.framework.common.objects.OperationOptionsBuilder;

import org.identityconnectors.framework.common.objects.ResultsHandler;

import org.identityconnectors.framework.common.objects.Uid;

import org.identityconnectors.framework.common.objects.filter.AndFilter;

import

org.identityconnectors.framework.common.objects.filter.ContainsAllValuesFilt

er;

import org.identityconnectors.framework.common.objects.filter.Filter;

import org.identityconnectors.framework.common.objects.filter.NotFilter;

public class TestDelete extends Test {

public TestDelete() {

super();

}

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

private void delete(){

//get a list of ConnectorInfo from ConnectorInfoManager

List<ConnectorInfo> cInfos =

this.getCInfoManager().getConnectorInfos();

//iterate through the list

for(ConnectorInfo cInfo:cInfos){

//get the APIconfig

APIConfiguration apiConfig =

cInfo.createDefaultAPIConfiguration();

//set pool configurations

setPoolConfigurations(apiConfig);

//get the configProps i.e reference to Configuration on SPI

side

ConfigurationProperties configProps =

apiConfig.getConfigurationProperties();

//set up config props

this.setUpConfigurationProperties(configProps);

//get a reference to ConnectorFacadeFactory

ConnectorFacadeFactory facadeFactory =

ConnectorFacadeFactory.getInstance();

//create the connector facade (nothing but reference to our

Connector on SPI side)

ConnectorFacade connectorFacade =

facadeFactory.newInstance(apiConfig);

//lets delete the account with ID 3

connectorFacade.delete(ObjectClass.ACCOUNT, new Uid("3"),

null);

}

}

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

public static void main(String[] args){

new TestDelete().delete();

}

}

Run the stand alone java client

org.identityconnectors.flatfileconnector.test.operation.TestDelete Verify to see correct user data in the flat file.

6. Reconciliation

6.1. Introduction Reconciliation is a process of updating OIM with the user’s data available on the target system. ICF provides interfaces and classes to achieve different types of reconciliations like one-time, incremental, lookup, role etc…

6.2. Reconciliation using Search operation ICF provides org.identityconnectors.framework.spi.operations.SearchOp interface for performing recon operations as stated above. Before going ahead with recon, let us see details of SearchOp interface

6.2.1. SearchOp - org.identityconnectors.framework.spi.operations.SearchOp All the recon related operations are performed using search functionality provided by the ICF. Connector bundles which requires supporting recon operations, should implement this interface. This interface requires us to provide implementation for two methods:

FilterTranslator<T> createFilterTranslator(ObjectClass oclass,OperationOptions options) – for more information, please see section 6.2.2

void executeQuery(ObjectClass oclass,T query, ResultsHandler handler, OperationOptions options)

The runtime JAR i.e connector-framework-internal.jar (which is in turn called by OIM via ICF glue i.e. icf-oim-intg.jar) calls the executeQuery method for each native query returned by FilterTranslator. For example say we are searching for information for account ids for 2 and 3. This method would be called twice, once with query=2 and second time query=3. Using this query, the executeQuery implementation should search through the target and obtained results should be passed onto the ResultsHandler which is again part of the OIM ICF glue (icf-oim-intg.jar).

For information on how to implement this method for different recon operation, please see subsequent sections.

6.2.2. Filter Translator

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

The search filter provided on the OIM side may contain operators such ‘contains’ or ‘in’ or may also include ‘AND’, ‘OR’ etc…these may not be possible for the connector developer to implement using the native APIs of the target system. ICF provides a convenient way to implement these filters. By creating a FilterTranslator (by sub classing AbstractFilterTranslator), the connector developer declares that what filters are supported by this connector. For example if the target system allows searching only based on equality then our sub class instance of AbstractFilterTranslator should override protected String createEqualsExpression(EqualsFilter filter, boolean not) The worst scenario is to return null in the createFilterTranslator implementation, in that case connector code should return all values. This case can be utilized for full and look up recon operations. 6.2.3. Results Handler org.identityconnectors.framework.common.objects.ResultsHandler is a callback interface and provides a single method boolean handle(ConnectorObject obj). The connector API developer should implement this and do whatever the calling application wants to do. I.e. in OIM ICF glue (connector API implementation for OIM), reconciliation engine is called. The implementation is very specific to the calling application. However the connector SPI should make sure to call this API once the search completes. 6.2.4. Operation options org.identityconnectors.framework.common.objects.OperationOptions is a class which helps the connector API to pass on any arbitrary options to the SPI implementations. This is essentially a collection of option names. API/SPI developer should make use of org.identityconnectors.framework.common.objects.OperationOptionsBuilder to create an OperationOptions. Operation options has been used for supporting various recon operations. As we know reconciliation in OIM is executed using scheduled tasks and each task has its own task parameters. These task parameters are passed into executeQuery method of SearchOp interface as operation options. The connector SPI developer is responsible to get these options in executeQuery method implementation, check different options (scheduled task parameters) and based on this, implementation for either full, lookup, incremental recon can be provided. For more information, see below sections.

6.3. One time reconciliation

One-time reconciliation is a process of getting all the user details from target into OIM. The executeQuery implementation of the connector can be updated to check the values in operation options it gets as below:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

The above screen shot shows a code snippet from the executeQuery method of org.identityconnectors.flatfileconnector.FlatFileConnector. Here we see that by default, we perform full or one-time recon, but based on operation options we perform lookup recon as well. The important point to note here is that we could perform different searches (and there by perform different recons) based on the values present in operation options. These operation options need to be set with the scheduled task parameters in OIM. The code snippet which searches without using scripts (see section 7.2 for more information) for all the users on target in executeQuery of FlatFileConnector is shown below (notice that it has been implemented already for you in FlatFileConnector.java file):

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

6.4. Incremental reconciliation Incremental reconciliation is a process of updating OIM with target system details based on certain conditions latest token. Condition could be anything get all user details which have been modified after a particular time. In this lab, we have used ‘LatestToken’ i.e. from connector API side we pass on another operation option called ‘LatestToken’ and on connector SPI side, we check for this value and if present, we call a different API called getIncrementalSearchResult(). Note: For simplicity, we are sending the same search result for full and incremental recon. The code snippet which searches without using scripts (see section 7.2 for more information) for users on target in executeQuery of FlatFileConnector is shown below:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

6.5. Lookup reconciliation Lookup reconciliation is a process of getting data from target system, which could be in turn used by OIM for provisioning. Again we can make use of search operation to perform lookup recon. In this lab, we are trying to find out different ‘Roles’ values that could be used while provisioning process. On API side, we set an operation option ‘Roles’ and on SPI side; we check if this is present in the executeQuery method and perform different operations accordingly. The code snippet which searches for roles on target in executeQuery of FlatFileConnector is shown below:

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

6.6. Role Reconciliation

ICF side implementation is the same as Lookup reconciliation. See above section 6.5

6.7. Test

One-time reconciliation: Back in FlatFileConnectorTestProject project, create TestFullRecon Java Class in package org.identityconnectors.flatfileconnector.test.operation Copy and paste the following java code:

package org.identityconnectors.flatfileconnector.test.operation;

import java.util.Collection;

import java.util.HashSet;

import java.util.List;

import org.identityconnectors.framework.api.APIConfiguration;

import org.identityconnectors.framework.api.ConfigurationProperties;

import org.identityconnectors.framework.api.ConnectorFacade;

import org.identityconnectors.framework.api.ConnectorFacadeFactory;

import org.identityconnectors.framework.api.ConnectorInfo;

import org.identityconnectors.framework.common.objects.ObjectClass;

import org.identityconnectors.framework.common.objects.OperationOptions;

import

org.identityconnectors.framework.common.objects.OperationOptionsBuilder;

public class TestFullRecon extends Test{

public TestFullRecon() {

super();

}

void search(){

//get a list of ConnectorInfo from ConnectorInfoManager

List<ConnectorInfo> cInfos =

this.getCInfoManager().getConnectorInfos();

//iterate through the list

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

for(ConnectorInfo cInfo:cInfos){

//get the APIconfig

APIConfiguration apiConfig =

cInfo.createDefaultAPIConfiguration();

//set pool configurations

setPoolConfigurations(apiConfig);

//get the configProps i.e reference to Configuration on SPI side

ConfigurationProperties configProps =

apiConfig.getConfigurationProperties();

//set up config props

this.setUpConfigurationProperties(configProps);

//get a reference to ConnectorFacadeFactory

ConnectorFacadeFactory facadeFactory =

ConnectorFacadeFactory.getInstance();

//create the connector facade (nothing but reference to our

Connector on SPI side)

ConnectorFacade connectorFacade =

facadeFactory.newInstance(apiConfig);

//Create an empty operation options..

//In the connector bundle executeQuery implementation, I am

checking if there is no extra

//options, I do full recon, so here I am sending empty options

OperationOptionsBuilder optionBuilder = new

OperationOptionsBuilder();

Collection<String> c = new HashSet<String>();

optionBuilder.setAttributesToGet(c);

OperationOptions options = optionBuilder.build();

connectorFacade.search(ObjectClass.ACCOUNT, null, new

FlatFileResultHandler(), options);

}

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

}

public static void main(String[] args){

new TestFullRecon().search();

}

}

Create also a Java Class named FlatFileResultHandler in package org.identityconnectors.flatfileconnector.test.operation using the following java code:

package org.identityconnectors.flatfileconnector.test.operation;

import java.util.Set;

import org.identityconnectors.framework.common.objects.Attribute;

import org.identityconnectors.framework.common.objects.ConnectorObject;

import org.identityconnectors.framework.common.objects.ResultsHandler;

public class FlatFileResultHandler implements ResultsHandler {

public FlatFileResultHandler() {

super();

}

public boolean handle(ConnectorObject connectorObject) {

Set<Attribute> attributes = connectorObject.getAttributes();

for(Attribute attribute:attributes){

if(!attribute.getName().equals("__UID__") &&

!attribute.getName().equals("__NAME__"))

{

System.out.println("Attr name: "+attribute.getName());

System.out.println("Attr value:

"+attribute.getValue().get(0).toString());

}

}

return true;

}

}

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Open and run the org.identityconnectors.flatfileconnector.test.operation.TestFullRecon. This calls the search API on the connector façade, which in turn calls the executeQuery on the Connector bundle. In the Results Handler implementation, we are just printing out the results. As described above in the section 6.2.3, the implementation is very specific to the calling application and is part of connector API. In the OIM ICF glue (icf-oim-intg.jar) we can the reconciliation engine to complete the recon event. Verify to see all data from the target in the JDeveloper console

Incremental reconciliation: Create TestIncrementalRecon Java Class in package org.identityconnectors.flatfileconnector.test.operation

package org.identityconnectors.flatfileconnector.test.operation;

import java.util.Collection;

import java.util.HashSet;

import java.util.List;

import org.identityconnectors.framework.api.APIConfiguration;

import org.identityconnectors.framework.api.ConfigurationProperties;

import org.identityconnectors.framework.api.ConnectorFacade;

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

import org.identityconnectors.framework.api.ConnectorFacadeFactory;

import org.identityconnectors.framework.api.ConnectorInfo;

import org.identityconnectors.framework.common.objects.ObjectClass;

import org.identityconnectors.framework.common.objects.OperationOptions;

import

org.identityconnectors.framework.common.objects.OperationOptionsBuilder;

public class TestIncrementalRecon extends Test{

public TestIncrementalRecon() {

super();

}

void search(){

//get a list of ConnectorInfo from ConnectorInfoManager

List<ConnectorInfo> cInfos =

this.getCInfoManager().getConnectorInfos();

//iterate through the list

for(ConnectorInfo cInfo:cInfos){

//get the APIconfig

APIConfiguration apiConfig =

cInfo.createDefaultAPIConfiguration();

//set pool configurations

setPoolConfigurations(apiConfig);

//get the configProps i.e reference to Configuration on SPI side

ConfigurationProperties configProps =

apiConfig.getConfigurationProperties();

//set up config props

this.setUpConfigurationProperties(configProps);

//get a reference to ConnectorFacadeFactory

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

ConnectorFacadeFactory facadeFactory =

ConnectorFacadeFactory.getInstance();

//create the connector facade (nothing but reference to our

Connector on SPI side)

ConnectorFacade connectorFacade =

facadeFactory.newInstance(apiConfig);

//Create an operation options containing 'LatestToken'

//In the connector bundle executeQuery implementation, we

perform

//incremental recon if this is present

OperationOptionsBuilder optionBuilder = new

OperationOptionsBuilder();

Collection<String> c = new HashSet<String>();

c.add("LatestToken");

c.add("FirstName");

c.add("lastName");

c.add("email");

c.add("AccountId");

optionBuilder.setAttributesToGet(c);

OperationOptions options = optionBuilder.build();

connectorFacade.search(ObjectClass.ACCOUNT, null, new

FlatFileResultHandler(), options);

}

}

public static void main(String[] args){

new TestIncrementalRecon().search();

}

}

Open and run the org.identityconnectors.flatfileconnector.test.operation. TestIncrementalRecon. This calls the search API on the connector façade, which in turn calls the executeQuery on the Connector bundle. Screen shot calling the search API on the façade is as shown

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Verify to see all data from the target in the JDeveloper console

Note: To keep code simple, we return same results as one-time reconciliation.

Lookup and Role reconciliation:

Create TestLookupRecon Java Class in package org.identityconnectors.flatfileconnector.test.operation

package org.identityconnectors.flatfileconnector.test.operation;

import java.util.Collection;

import java.util.HashSet;

import java.util.List;

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

import org.identityconnectors.framework.api.APIConfiguration;

import org.identityconnectors.framework.api.ConfigurationProperties;

import org.identityconnectors.framework.api.ConnectorFacade;

import org.identityconnectors.framework.api.ConnectorFacadeFactory;

import org.identityconnectors.framework.api.ConnectorInfo;

import org.identityconnectors.framework.common.objects.ObjectClass;

import org.identityconnectors.framework.common.objects.OperationOptions;

import

org.identityconnectors.framework.common.objects.OperationOptionsBuilder;

public class TestLookupRecon extends Test{

public TestLookupRecon() {

super();

}

void search(){

//get a list of ConnectorInfo from ConnectorInfoManager

List<ConnectorInfo> cInfos =

this.getCInfoManager().getConnectorInfos();

//iterate through the list

for(ConnectorInfo cInfo:cInfos){

//get the APIconfig

APIConfiguration apiConfig =

cInfo.createDefaultAPIConfiguration();

//set pool configurations

setPoolConfigurations(apiConfig);

//get the configProps i.e reference to Configuration on SPI side

ConfigurationProperties configProps =

apiConfig.getConfigurationProperties();

//set up config props

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

this.setUpConfigurationProperties(configProps);

//get a reference to ConnectorFacadeFactory

ConnectorFacadeFactory facadeFactory =

ConnectorFacadeFactory.getInstance();

//create the connector facade (nothing but reference to our

Connector on SPI side)

ConnectorFacade connectorFacade =

facadeFactory.newInstance(apiConfig);

//Create an empty operation options..

//In the connector bundle executeQuery implementation, we check

if we have to do lookup recon

//i.e. find different salutations available, so here we pass

'salutation' as operation option

OperationOptionsBuilder optionBuilder = new

OperationOptionsBuilder();

Collection<String> c = new HashSet<String>();

c.add("Roles");

optionBuilder.setAttributesToGet(c);

OperationOptions options = optionBuilder.build();

connectorFacade.search(ObjectClass.ACCOUNT, null, new

FlatFileResultHandler(), options);

}

}

public static void main(String[] args){

new TestLookupRecon().search();

}

}

Before running TestLookupRecon, have a look to the source code. This calls the search API on the connector façade, which in turn calls the executeQuery on the Connector bundle. Screen shot calling the search API on the façade is as shown.

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Double check the values of the path for the roles.txt in Test.java:

Create roles.txt in /app/connector_files with the following content:

Run org.identityconnectors.flatfileconnector.test.operation.TestLookupRecon. Verify to see all data from the target in the JDeveloper console

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

7. Advanced operations ICF provides a way for the calling application to execute scripts on target before and after provisioning. It also allows us to perform entire provisioning and reconciliation using scripts.

7.1. Provisioning using scripts In this process, we can actually make use any scripts which could be executed for provisioning. In this lab we make use of shell scripts for the same. Follow the below steps to achieve the same.

Update the test client org.identityconnectors.flatfileconnector.test.operation.Test to include the value to this new configuration property:

configProps.setPropertyValue("customScriptForProvisioning", new

File("/app/connector_files/createUser.sh"));

Create a script “createUser.sh” and place it on the connector server machine (/app/connector_files). This script should create a user if user is not existing else it should echo a message “account already exists”

Content of createUser.sh:

id=$1 firstName=$2 lastName=$3 email=$4 echo "**********Input values************" echo "id: "$id echo "First name:"=$firstName echo "Last name:"$lastName echo "email: "$email searchString="AccountId:"$id echo "Account ID: "$searchString content=`grep $searchString /app/connector_files/output.txt` echo "content = "$content if test "$content" != "" then echo "account already exists" exit 1 else echo "account with id $id does not exists, creating now.." echo "email:$4;FirstName:$2;lastName:$3;AccountId:$1" >> /app/connector_files/output.txt exit 0 fi

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Don't forget to change the properties of the file to make it executable:

Change the method for creating attributes in Test.java:

public Set<Attribute> createAttributes(String accountID){

Set<Attribute> attributes = new HashSet<Attribute>();

Attribute id = AttributeBuilder.build("AccountId",

accountID);

Attribute firstName =

AttributeBuilder.build("FirstName", "Robert");

Attribute lastName = AttributeBuilder.build("lastName",

"De Niro"+accountID);

Attribute email = AttributeBuilder.build("email",

"[email protected]");

attributes.add(id);

attributes.add(firstName);

attributes.add(lastName);

attributes.add(email);

return attributes;

}

Also hange the code in TestCreate.java:

//lets call the create operation on the facade

connectorFacade.create(ObjectClass.ACCOUNT,

createAttributes("4"), null);

// connectorFacade.create(ObjectClass.ACCOUNT,

createAttributes("2"), null);

// connectorFacade.create(ObjectClass.ACCOUNT,

createAttributes("3"), null);

Run the org.identityconnectors.flatfileconnector.test.operation.TestCreate client to see user details being created using the new script provided.

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Note: - This lab uses scripts only for create operation and uses java calls for update, enable, disable and delete operations. Check output.txt

7.2. Reconciliation using scripts Create a script “getAllUsers.sh” and place it on the connector server machine (/app/connector_files). This script gets all the users from the target.

content=`cat /app/connector_files/output.txt`

echo $content

Don't forget to allow executing the file as a program. Check that FlatFileConnectorConfiguration.java is including a configuration property.customScriptForRecon of type java.io.File custom script which will be used for reconciliation (search) operations. This is not a mandatory property and if not provided search would be performed using java APIs. Update the test client org.identityconnectors.flatfileconnector.test.operation.Test to include the value to this new configuration property:

configProps.setPropertyValue("customScriptForRecon", new File("/app/connector_files/getAllUsers.sh"));

Run the org.identityconnectors.flatfileconnector.test.operation.TestFullRecon client to see user details being searched using the new script provided Note: - This lab uses scripts only for one-time/full recon. For lookup, role java APIs has been used.

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

7.3. Extending the functionality of the connector

Create a new “Generic Project” FlatFileValidationAndTransformation under FlatFileConnectorApplication

7.3.1. Validation

Still in this project, create a new Java Class FlatFileValidator in package org.identityconnectors.flatfile.extension and copy and paste the following java code:

package org.identityconnectors.flatfile.extension;

import java.util.HashMap;

public class FlatFileValidator{

public boolean validate(HashMap hmUserDetails, HashMap

hmEntitlementDetails,String sField) {

boolean output=false;

System.out.println("Inside validate method of

FlatFileValidator...");

String smail = (String) hmUserDetails.get("Email");

for(int i=0;i<smail.length();i++){

if (smail.charAt(i) == '@'){

output=true;

break;

}

}

System.out.println("Value of output is-->"+output);

return output;

}

}

Argument hmUserDetails contains primary table field values

Argument hmEntitlementDetails contains child table field values

Argument sField is the field on which validation needs to be done

Implement validation logic as per your needs. In this example we are validating whether email contains @ or not.

7.3.2. Transformation

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Create a new Java class FlatFileTransformation in package org.identityconnectors.flatfile.extension and copy and paste the following java code:

package org.identityconnectors.flatfile.extension;

import java.util.HashMap;

public class FlatFileTransformation {

public Object transform(HashMap hmUserDetails, HashMap

hmEntitlementDetails,String sField) {

System.out.println("Inside flat file recon transformation");

String sFirstName= (String)hmUserDetails.get("FirstName");

String sLastName= (String)hmUserDetails.get("LastName");

String smail=sFirstName+"."+sLastName+"@myorg.com";

System.out.println("Mail is"+smail);

return smail;

}

} Argument hmUserDetails contains primary table field values

Argument hmEntitlementDetails contains child table field values

Argument sField is the field on which validation needs to be done

Implement transformation logic as per your needs. In this example we are concatenating the first and last name to form the email . When the users are reconciled from target.

Create build.xml and build.properties files:

Select FlatFileValidationAndTransformation project, right-click on New..., select General -> Ant and “Buildfile from Project”

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

Comment out the existing target name=buildJar <!--target name="buildJar" depends="compile " description="Create binary distribution"> <manifest file="MANIFEST.MF"> <attribute name="ConnectorBundle-FrameworkVersion" value="${ConnectorBundle-FrameworkVersion}"/> <attribute name="ConnectorBundle-Name" value="${ConnectorBundle-Name}"/> <attribute name="ConnectorBundle-Version" value="${MAJOR}.${MINOR}"/> </manifest> <jar jarfile="${basedir}/${ConnectorBundle-Name}-${MAJOR}.${MINOR}.jar" basedir="${output.dir}" manifest="MANIFEST.MF"/> </target--> Add the following lines: <target name="buildJar" depends="compile " description="Create binary distribution">

<jar jarfile="${basedir}/FlatFileValidationAndTransformation.jar"

basedir="${output.dir}"/>

</target>

Comment out the existing target name=all <!--target name="all" description="Build the project" depends="compile,copy,buildJar"/-->

And update the all target:

<target name="all" description="Build the project" depends="buildJar"/>

Build and create the FlatFileValidationAndTransformation.jar:

Right click on build.xml, select Run Ant Target and select all

This would generate the FlatFileValidationAndTransformation.jar

Click on Refresh to see the following content

Oracle Proprietary - Restricted to Personal Use in an Oracle partner training class

In the next lab, we will see how to glue this validation and transformation logic with OIM.

8. Conclusion In this lab, we have seen ICF architecture, connector SPI, connector API, OIM ICF glue, connector pooling, remote connector server, create, update, delete, enable, disable, full recon, incremental recon, lookup recon, role recon operations and testing each of these operations with different clients.