Wednesday, September 28, 2011

OIM 11g Event Handler example

This post shows an example of a post process event handler in OIM. The example is simple and it shows how the user profile can be updated from the event handler based on the information that is provided by OIM to the event handler.

Use case description: a UDF is created in the user profile and it will hold the user's 'Director'. To simplify the use case, the 'Director' will be the 'manager's manager'. In other words, the UDF will be populated with the information from two levels up in the management chain, the value to be used is the director's login.

The first step is to create the UDF that will hold the data. An authorization policy is also needed, otherwise it will not be possible to update the UDF using the APIs. All the steps below must be done in OIM logged as system administrator.

Creating the UDF:



Set the attribute size to 100:
Review and confirm:


Now it is time to the authorization policy. The authorization policy will give full control of the new attribute to the "System Administrators" group:


In the second step, make sure that the new attribute is selected:


Keep the assignment as it shows:


Make sure you select "System Administrators" in the assignment:


Review and finish:


After creating the authorization policy, you can verify its efectiviness navigating to the user creation page. Scrolling all the way down, you should be able to see the custom attribute:


With the UDF and the authorization policy in place, you can go ahead and deploy the plug-in. The plug-in is the code that will be invoked by OIM when executing the post process event handler. A plugin is basically a ZIP file that contains a XML file with the plug-in definition and the jar file containing the compiled plug-in code.

There are two options for deploying a plug-in: copying the ZIP file to $OIM_HOME/plugins folder and using the $OIM_HOME/plugin_utility/pluginregistration.xml ANT script. For development environments I use the first option, for clustered and production environment the second option is the way to go because it will deploy the plug-in to the database.

Below the XML with the plug-in definition:

<?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="oracle.iam.demo.eventhandlers.UserDirectorEventHandler" version="1.0" name="UserDirectorEventHandler"/>     
    </plugins>
</oimplugins>

And the plug-in code:

package oracle.iam.demo.eventhandlers;


import java.io.Serializable;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import oracle.iam.identity.exception.UserSearchException;
import oracle.iam.identity.usermgmt.api.UserManager;
import oracle.iam.identity.usermgmt.api.UserManagerConstants.AttributeName;
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.entitymgr.EntityManager;
import oracle.iam.platform.entitymgr.vo.SearchCriteria;
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;


public class UserDirectorEventHandler implements PostProcessHandler {

    public UserDirectorEventHandler() {}

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

    @Override
    public boolean cancel(long arg0, long arg1, AbstractGenericOrchestration arg2) {
        return false;
    }

    @Override
    public void compensate(long arg0, long arg1, AbstractGenericOrchestration arg2) {
    }
    
    public EventResult execute(long l1, long l2, Orchestration orch) {
        
        System.out.println("Executing CRUD operation ");
        HashMap<String, Serializable> parameters = orch.getParameters();
              
        try {
             executeEvent(parameters,orch.getTarget().getType(), orch.getTarget().getEntityId());
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return new EventResult();
    }
    
    public BulkEventResult execute(long l1, long l2, BulkOrchestration orch) {
      
        System.out.println("Executing BULK operation ");
        HashMap<String, Serializable>[] bulkParameters = orch.getBulkParameters();
        
        String[] entityIds = orch.getTarget().getAllEntityId();
        
        for (int i = 0; i < bulkParameters.length; i++) {
  
            try {
               executeEvent(bulkParameters[i],orch.getTarget().getType(), entityIds[i]);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return new BulkEventResult();
    }
    
    private List<User> getUser(String fieldName, String fieldValue, String retAttr) throws AccessDeniedException,
                                                      UserSearchException {
        
        System.out.println("Searching users with "+fieldName+"="+fieldValue);
        
        Set <String>retAttributes = new HashSet<String>();
        retAttributes.add(retAttr);
        
        SearchCriteria srchCriteria = new SearchCriteria(fieldName,fieldValue, SearchCriteria.Operator.EQUAL);

        UserManager mgr = Platform.getService(UserManager.class);

        return mgr.search(srchCriteria, retAttributes, null);
    }

    private void executeEvent(HashMap parameterHashMap, String targetType, String targetId) {
        
        System.out.println(parameterHashMap);
             
        Long managerLogin = (parameterHashMap.get(AttributeName.MANAGER_KEY.getId()) instanceof ContextAware)
                ? (Long) ((ContextAware) parameterHashMap.get(AttributeName.MANAGER_KEY.getId())).getObjectValue()
                : (Long) parameterHashMap.get(AttributeName.MANAGER_KEY.getId());
                
        System.out.println("managerLogin "+managerLogin);
        if (managerLogin != null) {
            
            try {
                Long directorKey = null;
                String directorLogin = null;
                
                List<User> users = this.getUser(AttributeName.USER_KEY.getId(), managerLogin.toString(), AttributeName.MANAGER_KEY.getId());
                
                if (users.size()==1) {
                    directorKey = (Long) users.get(0).getAttribute(AttributeName.MANAGER_KEY.getId());
                    
                    users = this.getUser(AttributeName.USER_KEY.getId(), directorKey.toString(), AttributeName.USER_LOGIN.getId());

                    directorLogin = (String) users.get(0).getAttribute(AttributeName.USER_LOGIN.getId());
                }
                
                System.out.println("directorLogin "+directorLogin);

                if (directorLogin != null) {

                    HashMap<String, Object> mapAttrs = new HashMap<String, Object>();
                    mapAttrs.put("Director", directorLogin);
                    EntityManager entMgr = Platform.getService(EntityManager.class);
                        
                    entMgr.modifyEntity(targetType,targetId, mapAttrs);                    
                }
            }
            catch (Exception e) {
                System.out.println("Error updating user director "+e.getMessage());
                e.printStackTrace();
            }           
        }
    }
    
    
}


And finally the event handler 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="oracle.iam.demo.eventhandlers.UserDirectorEventHandler" entity-type="User" operation="CREATE" name="UserDirectorUpdate" stage="postprocess" order="1000" sync="TRUE"/>
</eventhandlers>

A few comments about this example and event handlers:

  • The event handler is defined for post updates only. It could also be used as post create by modifying the event handler XML definition.
  • The use of the class oracle.iam.platform.entitymgr.EntityManager for updating the user profile prevents OIM from triggering a second orchestration event after the user gets updated by. If a second execution is needed, then the oracle.iam.identity.usermgmt.api.UserManager class must be used for updating the user (keep in mind that in this case the event handler code will have to have controls to prevent infinite loops in the event handler invocation).
  • OIM sends only the modified data in the Orchestration argument sent to the event handler. The event handler in this example depends on the presence of the 'Manager Key' field in the Orchestration data, and it will update the user only when this field is present. 
  • Keep in mind that post process event handlers can have impact on overall OIM performance, so try to keep them as 'little' as possible.
  • The example in this post works for OIM 11g PS1 (11.1.1.5). In order to deploy the same example to a 11.1.1.3 environment, it is necessary to delete the namespaces tags from the event handler XML.

Check the Oracle Identity Manager Academy for other OIM 11g related posts

10 comments:

  1. Hi,

    User event handlers are described in detailed in the docs. Thus would appreciate if you can share some example on how to define the event handler xml for say Request where we want to have a pre insert adapter on the request.

    Thanks,
    Bikash Bagaria

    ReplyDelete
  2. This is fine but shows only event handlers for Users (already covered in the documentation). I would like to see event handlers example on say process form or say requests. 9x provided us a way of attaching event handlers on these as well and that covered lots of user cases, but 11G documentation lacks any mention of those. Another example would be nice about how to find order of the OOTB User event handlers? There might be a scenario where we would want to invoke our event handler before access policy is triggered on user post process from UI and recon. Document does not talk about those as well.

    ReplyDelete
    Replies
    1. As far as the event handlers on process forms go, those are not available and I do not know if the orchestration framework will be extended to include those. The documentation clearly states that the event handlers are there to extend users and roles management. Although you mentioned that this would cover a lot of use cases, I have not seen many customers using this feature.

      Request framework is totally different now and there is a lot of possibilities to replace event handlers on request. The use of SOA gives a lot of flexibility.

      Unfortunately OIM current documentation (11.1.1.x) does not provide much information on the OOTB event handlers. The order you are looking for can be found directly in the OOTB XML files that define the event handlers. Other option you may try is to open a service request with Oracle Support to get the information you are looking for.

      Delete
  3. Hi,
    Can you please explain what is meant by "The use of the class oracle.iam.platform.entitymgr.EntityManager for updating the user profile prevents OIM from triggering a second orchestration event after the user gets updated by. "

    I am confused by second orchestration here.Actually I am having some issues with entity manager.Please email me at smukul.mhjn@gmail.com if you can so that we can discuss

    ReplyDelete
    Replies
    1. There are two ways of updating a user: UserManager or EntityManager API. When you use the 'UserManager' API, OIM will start an orchestration cycle at the call to 'UserManager.modify' (if you are doing the call from an event handler, this call would be a second orchestration). If you use 'EntityManager', no orchestration will be started.

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hi Daniel,
    I have developed a Post process event handler to generate a Email for users. My problem is that when user is updated or created the code trigger both execute nad bulk execute. so its affecting my code functionality.please help me .

    ReplyDelete
    Replies
    1. Animesh,

      How are you creating/updating the user? Through recon?

      If yes, the post process will first trigger for the recon operation (on the bulk execute). Then it generates user's email, then it updates the user. Then because the user got updated, there is a new orchestration cycle, now on the execute method. This is the way it works.

      So in your custom post process, you have to have controls over the execution to prevent doing the same thing twice.

      Hope this helps

      Daniel

      Delete
  6. Hi, where I can find the list of values about: entity-type and related operation? I mean: entity-type="User" has operation="CREATE" and "MODIFY" and "DISABLE" and "ENABLE" and "DELETE"; is it right? Are there others operations? Also are there additional entity-types (e.g. ROLE or ACCESSPOLICY)?

    Gabriele

    ReplyDelete
    Replies
    1. OIM Developer's Guide mentions:

      http://docs.oracle.com/cd/E21764_01/doc.1111/e14309/oper.htm

      from the link above: "You can customize the consequences of user management operations such as create, update, delete, enable, disable, lock, unlock, and change password"

      Role, RoleUser, Organization are other entity types. Access policy is not among them.

      Hope this helps

      Daniel

      Delete

Note: Only a member of this blog may post a comment.