Friday, March 18, 2016

Developing Connector Using Identity Connector Framework

Description

This post covers the process to develop custom connector for the database using Identity Connector Framework . It includes the information about important ICF classes and interfaces, connector bundle, connector server and code sample for implementing database connector

The development of Connector is divided into following
  1. Development Database Connector  using ICF SPI (Service Provider Interface)
  2. Uploading the Connector Bundle into Oracle Identity Manager Database or Connector Server.
Development Database Connector Using ICF SPI
Connector JARs –   
  1. ICF provides two JARs namely connector-framework.jar and connectorframework-internal.jar.
  2. 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.
         The jars mentioned are available on OIM Host Machine and available on below locations
             $IDM_HOME/connectors/icf/framework

  • Create a project in JDeveloper and add the above mentioned jar files in Library and Class-path


 Defining the Configuration properties

  1. Define the configuration properties to capture the config data from OIM to Connector to establish the connectivity to the oracle database.  
  2. For demo i have used 4 config properties are driver, url, userName and password. 
  3. The driver property holds the jdbc driver, url holds the jdbc url, username holds the jdbc user name and password holds the jdbc password
Description  
Implement the configuration class for the Flat File Connector by extending the org.identityconnectors.framework.spi.AbstractConfiguration base class
package com.iam.oracle.custom;  
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.spi.AbstractConfiguration;
import org.identityconnectors.framework.spi.ConfigurationProperty;
import org.identityconnectors.framework.spi.ConfigurationProperty
public class DatabaseConfiguration extends AbstractConfiguration {
    public DatabaseConfiguration() {
        super();
    }
    private String driver;
     private String url;
     private String userName;
     private String password;
     public String getDriver() {
      return driver;
     }
   @ConfigurationProperty(order=1,displayMessageKey="driver_display",helpMessageKey="driver_help",required=true)
     public void setDriver(String driver) {
      this.driver = driver;
     }
     public String getUrl() {
      return url;
     }
  @ConfigurationProperty(order=2,displayMessageKey="url_display",helpMessageKey="url_help",required=true)
     public void setUrl(String url) {
      this.url = url;
     }
     public String getUserName() {
      return userName;
     }
    @ConfigurationProperty(order=3,displayMessageKey="userName_display",helpMessageKey="userName_help",required=true)
     public void setUserName(String userName) {
      this.userName = userName;
     }
     public String getPassword() {
      return password;
     }
   @ConfigurationProperty(order=4,displayMessageKey="password_display",helpMessageKey="password_help",required=true,confidential=true)
     public void setPassword(String password) {
      this.password = password;
     }
   public void validate() 
    {
    }}
 Connector operation interfaces
The following interfaces needs to be implemented to develop the custom ICF Connector. They are
PoolableConnector 
  1. This interface is required to implement the Pooled the connector instance.
SearchOp<String> 
  1. This interface is required to reconcile the users from the target system.
UpdateOp 
  1. The interface is required to implement the attribute changes from OIM to target system.
  2.  The attribute changes are updating the user attribute, enable and disable the user.
DeleteOp 
  1. The interface is required to  De-provision  the user from target system
CreateOp
  1. The interface is required  to provisioning to the user to the target system.
 SyncOp
  1. The interface is required to fetch the changes from the target system and synchronize with OIM.
GetApiOp
  1. The interface is required to get the user based on the uid. SchemaOp
  2. The interface is required to build the metadata in the OIM.



Sample Connector Class
package com.iam.oracle.custom;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.identityconnectors.framework.api.operations.GetApiOp;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.ResultsHandler;
import org.identityconnectors.framework.common.objects.Schema;
import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.common.objects.filter.FilterTranslator;
import org.identityconnectors.framework.spi.Configuration;
import org.identityconnectors.framework.spi.ConnectorClass;
import org.identityconnectors.framework.spi.PoolableConnector;
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.SearchOp;
import org.identityconnectors.framework.spi.operations.UpdateOp;
@ConnectorClass(configurationClass = DatabaseConfiguration.class,
                displayNameKey = "displayName")
public class DatabaseConnector implements SchemaOp, CreateOp, DeleteOp,
                                          UpdateOp,
                                          SearchOp<Map<String, String>>,
                                          GetApiOp, PoolableConnector {
    private static final Logger dbLogger =
        Logger.getLogger("DatabaseConnector");
    private DatabaseConfiguration config;
    public DatabaseConnector() {
        super();
    }
    public Schema schema() {
        return null;
    }
    public Uid create(ObjectClass objectClass, Set<Attribute> set,
                      OperationOptions operationOptions) {
/* This method is called by Create Object method in ICF APIobjectClass is either account or groupset contains the attribute values to be passed onto targetoperationOptions is null */ 
        return null;
    }
    public void delete(ObjectClass objectClass, Uid uid,
                       OperationOptions operationOptions) {
    }
    public Uid update(ObjectClass objectClass, Uid uid, Set<Attribute> set,
                      OperationOptions operationOptions) {
        return null;
    }
    public FilterTranslator<Map<String, String>> createFilterTranslator(ObjectClass objectClass,
                                                                        OperationOptions operationOptions) {
        return null;
    }
    public void executeQuery(ObjectClass objectClass, Map<String, String> map,
                             ResultsHandler resultsHandler,
                             OperationOptions operationOptions) {
    }
    public ConnectorObject getObject(ObjectClass objectClass, Uid uid,
                                     OperationOptions operationOptions) {
        return null;
    }
    public void checkAlive() {
/* use this method to check is active or not from pool of connections*/ 
    }
    public Configuration getConfiguration() {
        return null;
    }
    public void init(Configuration configuration) {
        dbLogger.fine(this.getClass().toString());
        dbLogger.fine("Invoking the Init Method Start");
        this.config = (DatabaseConfiguration)Configuration;
        Connection conn = null;
        try {
            Class.forName("oracle.jdbc.OracleDriver");

            String url = config.getUrl();
            String userName = config.getUserName();
            String password = config.getPassword();
            String driver = config.getDriver();
            /* Driver example jdbc:oracle:thin
             * userName example user for database Connection
             * password for database connection
             * url should be hostname:port/Servicename*/
            String dbURL =
                driver + ":" + userName + "/" + password + "@" + url;
            conn = DriverManager.getConnection(dbURL);
            dbLogger.fine("Connection established");

        } catch (SQLException e) {
            dbLogger.fine("initializing the data base error");
            throw new ConnectorException(e.getMessage());
        } catch (ClassNotFoundException e) {
            dbLogger.fine("ClassNotFoundException");

            throw new ConnectorException(e.getMessage());
        }
        dbLogger.fine("Invoking the Init Method End")
    }
    public void dispose() {
/* Use JDBC API to close the connection*/ 
    }
}

 Create Jar file using ant Script
Creating the database connector bundle
 Right click on DatabaseConnector , click New, select Ant and Buildfile from project and  click Ok 

        Click Ok again on the next screen



Right click on ICFDatabaseConnector Project and select Project Properties and under Ant, select Properties and add the following and 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="${ConnectorBundleFrameworkVersion}"/>
<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>


And also change 

  <target name="all" description="Build the project" depends="compile,copy"/>
 to

  <target name="all" description="Build the project" depends="compile,copy,buildJar"/> in build.xml


The directory structure after creation looks like below:-





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

Copy the newly created database file bundle JAR to the connector server bundle directory and re-start the connector server. 


Deploy the Jar File

  1. Login to the OIM Server.
  2.  Go to the DOMAIN_HOME/bin directory and execute source ./setDomainEnv.sh file. After executing the file , it will set the classpath.
  3.  Go to the OIM_HOME/server/bin directory an execute the 
  4. UploadJars.sh [-username <username>] [-password <password>] [-serverURL <t3://oimhostname:oimportno>] [-ctxFactory <weblogic.jndi.WLInitialContextFactory>] [- [-ICFBundle <Location of the ICF Bundle Jar>]



It will deploy the ICFBundle into the OIM repository.

--Nagaraju Gorrepati





No comments:

Post a Comment

Other Posts