Tuesday, June 4, 2013

Post-Process EventHandler Example: DecryptedPasswordInProfile

Version: Oracle Identity Manager 11g R1
Description: An example of a custom post-process event handler. This plugin is only for demonstration purposes.  The orchestration object parameter in the execute method contains useful information you may need to utilize. Here are several orchestration methods to make note of for post-process:
getParameters() : This method only gets the new changes made.
getOperation(): Gets the operation that is being performed. Some operations are given below:
  • CREATE = whenever a new user is being created
  • CHANGE_PASSWORD = whenever the user 's password is changed
  • MODIFY = whenever any changes are being made to a user's OIM Profile
getInterEventData(): contains the current state and new state of the entire user's profile. You can even determine the user who is making the changes with the "usr_updateby" attribute  in the new state of the user's profile.

You can find the final plugin package and the source code here.

Summary for Deploying the Plugin
1. Create jar file, plugin.xml, and metadata xml.
2. Create a zip with the following structure:
[NameOfPlugin].zip
     |____plugin.xml
     |____lib/            
           |_____[yourJarFile].jar

3. Modify the ant.properties file located in "[IDM_HOME]/server/plugin_utility".
Then register your plugin by executing "ant -f pluginregistration.xml register".

4. Modify the weblogic.properties located in "[IDM_HOME]/server/bin".
Then push the metadata into MDS by using the weblogicImportMetadata.sh utiltiy.

5. Purge the cache. The utility is in "[IDM_HOME]/server/bin".
./PurgeCache.sh ALL

Project Structure



Source Code
package oim.eventhandler.postprocess;

import com.thortech.xl.crypto.tcCryptoException;
import com.thortech.xl.crypto.tcCryptoUtil;
import java.io.Serializable;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import oracle.core.ojdl.logging.ODLLogger;
import oracle.iam.identity.exception.NoSuchUserException;
import oracle.iam.identity.exception.SearchKeyNotUniqueException;
import oracle.iam.identity.exception.UserModifyException;
import oracle.iam.identity.exception.ValidationFailedException;
import oracle.iam.identity.usermgmt.api.UserManager;
import oracle.iam.identity.usermgmt.vo.User;
import oracle.iam.platform.Platform;
import oracle.iam.platform.authz.exception.AccessDeniedException;
import oracle.iam.platform.context.ContextAware;
import oracle.iam.platform.kernel.spi.PostProcessHandler;
import oracle.iam.platform.kernel.vo.AbstractGenericOrchestration;
import oracle.iam.platform.kernel.vo.BulkEventResult;
import oracle.iam.platform.kernel.vo.BulkOrchestration;
import oracle.iam.platform.kernel.vo.EventResult;
import oracle.iam.platform.kernel.vo.Orchestration;

/**
 * Event Handler Type: Post-Process
 * Triggers: Whenever a password is changed. Operation Type is defined
 * in the meta-data XML file for this event handler.
 * Action: Set the previous and current password to be values
 * for selected OIM User attributes.
 * 
 * Note: This should never be done in practice. This
 * is only meant for a learning tutorial about event handlers.
 */
public class DecryptedPasswordInProfile implements PostProcessHandler
{
    //ODL Logger, which OIM uses by default
    //ojdl.jar can be found in MIDDLEWAREHOME/oracle_common/modules/oracle.odl_11.1.1
    public static final Logger logger = ODLLogger.getLogger(DecryptedPasswordInProfile.class.getName());

    /**
     * @param processId     OIM.ORCHEVENTS.ProcessId
     * @param eventId       OIM.ORCHEVENTS.ID
     * @param orchestration Holds useful data
     */
    @Override
    public EventResult execute(long processId, long eventId, Orchestration orchestration) 
    {
        logger.info("Start DecryptedPasswordInProfile execute method: ");
        try
        {
            logger.info(String.format("Start execute() with ProcessId: %s and EventId %s",processId, eventId));
            HashMap<String, Serializable> newParameters = orchestration.getParameters(); //contains only the new values
            HashMap<String, Serializable> interParameters = orchestration.getInterEventData(); //contains old and new values of user
            logger.info(String.format("Inter Parameters: %s ", interParameters));
            logger.info(String.format("New Parameters: %s ", newParameters));

            //Check if the user's password is being modified
            //If it is, it should be in the newParameters object
            if(newParameters.get("usr_password") != null)
            {
                User currentUserState = (User) interParameters.get("CURRENT_USER"); //Get target user's current (old) info state
                String userKey = orchestration.getTarget().getEntityId(); //Get the target user's key
                String userLogin = currentUserState.getLogin(); //Get target user's login

                String oldPasswordEncrypted = (currentUserState.getAttribute("usr_password") instanceof ContextAware)
                    ? (String) ((ContextAware) currentUserState.getAttribute("usr_password")).getObjectValue()
                    : (String) currentUserState.getAttribute("usr_password");
                //Decrypt password using the default secret key
                String oldPasswordDecrypted = tcCryptoUtil.decrypt(oldPasswordEncrypted, "DBSecretKey"); 
                String newPasswordEncrypted = getParamaterValue(newParameters, "usr_password"); 
                String newPasswordDecrypted = tcCryptoUtil.decrypt(newPasswordEncrypted, "DBSecretKey");

                logger.info(String.format("User's password is being changed. User Key: %s, Login: %s", userKey, userLogin));
                logger.info(String.format("Old Password: %s\nNew Password: %s\n", oldPasswordDecrypted, newPasswordDecrypted));

                UserManager usrOps = Platform.getService(UserManager.class); //Get services from UserManager class
                HashMap modParams = new HashMap(); //contains the attaributes to modify
                modParams.put("State", oldPasswordDecrypted); //set State attribute value to old password 
                modParams.put("Street", newPasswordDecrypted); //set Street attrinute value to new passord
                User modUser = new User(userKey, modParams);
                usrOps.modify("usr_key", userKey, modUser); //modify the target user
                logger.info("User is modified.");
            }
        } 

        catch (ValidationFailedException ex) {logger.log(Level.SEVERE,"",ex);}
        catch (AccessDeniedException ex) {logger.log(Level.SEVERE,"",ex);}
        catch (UserModifyException ex) {logger.log(Level.SEVERE,"",ex);} 
        catch (NoSuchUserException ex) {logger.log(Level.SEVERE,"",ex);}
        catch (SearchKeyNotUniqueException ex) {logger.log(Level.SEVERE,"",ex);}
        catch (tcCryptoException ex) {logger.log(Level.SEVERE,"",ex);}
        catch (Exception ex) {logger.log(Level.SEVERE,"",ex);}

        return new EventResult();
    }

    /**
     * ContextAware object is obtained when the actor is a regular user.
     * If the actor is an administrator, the exact value of the attribute is obtained.
     * @param parameters    parameters from the orchestration object
     * @param key   name of User Attribute in OIM Profile or column in USR table
     * @return value of the corresponding key in parameters
     */
    private String getParamaterValue(HashMap<String, Serializable> parameters, String key) 
    {
        String value = (parameters.get(key) instanceof ContextAware)
        ? (String) ((ContextAware) parameters.get(key)).getObjectValue()
        : (String) parameters.get(key);
        return value;
    }

    @Override
    public BulkEventResult execute(long l, long l1, BulkOrchestration bo) 
    {
        return null;
    }

    @Override
    public void compensate(long l, long l1, AbstractGenericOrchestration ago) 
    {

    }

    @Override
    public boolean cancel(long l, long l1, AbstractGenericOrchestration ago) 
    {
        return false;
    }

    @Override
    public void initialize(HashMap<String, String> hm) 
    {

    }
}

Plugin xml
Name: plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<oimplugins xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <plugins pluginpoint="oracle.iam.platform.kernel.spi.EventHandler">
        <plugin pluginclass="oim.eventhandler.postprocess.DecryptedPasswordInProfile" version="1.0" name="DecryptedPasswordInProfile"/>     
    </plugins>
</oimplugins>

Plugin Zip Structure
zip -r DecrpytedPasswordEH.zip lib/ plugin.xml 
  adding: lib/ (stored 0%)
  adding: lib/PostProcessEventHandler.jar (deflated 60%)
  adding: plugin.xml (deflated 36%)

Event Handler Metadata
Name: decryptedPasswordEH.xml
<?xml version="1.0" encoding="UTF-8"?>
<eventhandlers xmlns="http://www.oracle.com/schema/oim/platform/kernel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.oracle.com/schema/oim/platform/kernel orchestration-handlers.xsd">  
   <action-handler class="oim.eventhandler.postprocess.DecryptedPasswordInProfile" entity-type="User" operation="CHANGE_PASSWORD" name="DecryptedPasswordInProfile" stage="postprocess" order="1050" sync="TRUE"/>  
</eventhandlers>

ant.properties
############################################################
##      Oracle OIM plugin registration
##
## This file contains properties used
## for the plugin registration utility for registering
## and unregistering plugins
##
## Depending on your configuration you may have to change
## these properties.
##
############################################################

# The installation directory for WLS
wls.home=/home/oracle/Oracle/Middleware/wlserver_10.3

# The home directory for OIM. In case of shiphome its same as install directory
oim.home=/home/oracle/Oracle/Middleware/Oracle_IDM1/server

#login file name with path.
login.config=${oim.home}/config/authwl.conf

OIM.Username=xelsysadm
ServerURL=t3://localhost:14000
PluginZipToRegister=/home/oracle/DecryptedPasswordEH/DecrpytedPasswordEH.zip

weblogic.properties
The full path of event handler's metadata for this example is "/home/oracle/event_handlers_metadata/metadata/decryptedPasswordEH.xml" .
# Weblogic Server Name on which OIM application is running 

wls_servername=oim_server1

# If you are importing or exporting any out of box event handlers, value is oim. 
# For rest of the out of box metadata, value is OIMMetadata. 
# If you are importing or exporting any custom data, always use application name as OIMMetadata.

application_name=OIMMetadata

# Directory location from which XML file should be imported.
# Lets say I want to import User.xml and it is in the location /scratc/asmaram/temp/oim/file/User.xml, 
# I should give from location value as /scratc/asmaram/temp/oim. Make sure no other files exist 
# in this folder or in its sub folders. Import utility tries to recursively import all the files under the 
# from location folder. This property is only used by weblogicImportMetadata.sh

metadata_from_loc=/home/oracle/event_handlers_metadata

# Directory location to which XML file should be exported to

metadata_to_loc=/home/oracle/exportMetadata

# For example /file/User.xml to export user entity definition. You can specify multiple xml files as comma separated values.
# This property is only used by weblogicExportMetadata.sh and weblogicDeleteMetadata.sh scripts

metadata_files=/metadata/decryptedPasswordEH.xml

2 comments:

  1. please let me know the location of created custom event handlers and adapter...after configuration

    ReplyDelete
  2. Thank you ..Your tutorial helped me fix my issue today.

    ReplyDelete