lab 17: create an oim flat file connector using …...lab 17: create an oim flat file connector...
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",
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
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
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",
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.