Wednesday, December 19, 2012

Steps to create a custom notification event in OIM 11g R2 and triggering it using OIMClient API


First step is to create a custom notification Event metadata. To achieve this create a custom event xml metadata file. For example if we want to define a custom notification when a user modifies his/her Challenge Questions the custom event xml file would look like:

<?xml version='1.0' encoding='UTF-8'?>
<Events xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../notification/metadata/NotificationEvent.xsd" xmlns="http://xmlns.oracle.com/oim/notificationevent">
<EventType name="SetChallengeQuestions">
<Resolver class="custom.oim.notification.setchallengequestions.SetChallengeQuestionsResolver">
<Param Name="SetChallengeQuestions" DataType="X2-Entity" EntityName="SetChallengeQuestions"/>
</Resolver>
</EventType>
</Events>

Note: you can save the xml file with any name but to avoid confusion we have named it SetChallengeQuestions.xml file
Now import this xml file using weblogicImportMetadata.sh file located at $OIM_ORACLE_HOME /server/bin
Note: Before executing weblogicImportMetadata.sh you need to modify the weblogic.properties located at $OIM_ORACLE_HOME/server/bin directory. The property file must have the below properties:

wls_servername=oim_server1
application_name = OIMMetadata
metadata_from_loc=/home/oracle/notification
metadata_to_loc=/home/oracle/notification
metadata_files=/metadata/iam-features-selfservice/notification/SetChallengeQuestions.xml

Run weblogicImportMetadata.sh script to import the metadata files.
Note: OIM_ORACLE_HOME environment variable must be set before running this command.

Now create a SetChallengeQuestionsResolver.java class compile it and create a jar file with this class file



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import oracle.iam.identity.usermgmt.api.UserManager;
import oracle.iam.identity.usermgmt.vo.User;
import oracle.iam.identity.utils.Utils;
import oracle.iam.notification.impl.NotificationEventResolver;
import oracle.iam.notification.vo.NotificationAttribute;
import oracle.iam.passwordmgmt.vo.Constants;
import oracle.iam.platform.Platform;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class SetChallengeQuestionsResolver implements NotificationEventResolver{
    protected static final Logger logger= LoggerFactory.getLogger( SetChallengeQuestionsResolver.class);
    
    public List<NotificationAttribute> getAvailableData(String string,
                                                        Map<String, Object> map) {
        return Collections.emptyList();
    }

    public HashMap<String, Object> getReplacedData(String string, Map<String, Object> map) throws Exception{
        HashMap<String, Object> resolvedData = new HashMap();
        String userLoginId=null;
        Set<String> userRetAttrs = new HashSet<String>(); 
        if (!Utils.isMTFriendly()) {
            resolvedData.put("userLoginId", map.get("userLoginId"));         
        }
        else {
            resolvedData.put("userLoginId", map.get("Non MT User Login"));
            resolvedData.put("tenantName", map.get(Constants.TENANT_NAME));
        }
        
        userLoginId = String.valueOf(resolvedData.get("userLoginId"));           
        userRetAttrs.add("First Name");
        userRetAttrs.add("Last Name");
        UserManager userMgr = Platform.getService(UserManager.class); 
        User user;        
        user = userMgr.getDetails(userLoginId, userRetAttrs, true);
        
        logger.debug("First Name inside SetChallengeQuestionsResolver:   "+user.getFirstName());
        logger.debug("Last Name:   "+user.getLastName());    
        resolvedData.put("firstName", user.getFirstName());
        resolvedData.put("lastName", user.getLastName());       
         
        return resolvedData;
    }
}


Define a plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<oimplugins xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <plugins pluginpoint="oracle.iam.notification.impl.NotificationEventResolver">
        <plugin pluginclass= "custom.oim.notification.setchallengequestions.SetChallengeQuestionsResolver" version="1.0" name="CustomSetChallengeQuestionsResolver"/>    
    </plugins>
</oimplugins>

Create a Zip file named setchallengequestionsresolver.zip file that contains the lib folder (which the jar file you have created earlier) Resources folder (empty) and the plugin.xml file
Copy the setchallengequestionsresolver.zip file to the $OIM_ORACLE_HOME/server/plugins directory.
Create a new email template for this event.
Login to OIM System Administration Console using the following URL. http://<OIM Server Hostname>:<OIM server Port>/sysadmin and the following connections page will appear as shown in the below screen shot.
Note 1: The OIM Server Hostname and OIM server Port values have to be replaced with actual values depending on the environment you are carrying out these steps.
Note 2: The sample URL for accessing OIM System Administration screen looks like this http://idmlab.com:14000/sysadmin.

Click on Notification under System Management and new window will open.
Click on Add Notification button and “Create Template” tab will appear.
Enter “Template Name” value as “SetChallengeQuestionsTemplate”, enter some description text, Select “SetChallengeQuestions” as the “Available Event”, Select “UTF-8” as the “Encoding”, “Message Subject” as “Information Updated”, “Type” as “HTML”, “Short Message” as “Information Updated”, and “Long message” as the below html text and then click Save.

<html><head></head>  <body>   
                    <p>
Dear $firstName $lastName,
</p> <p>
Your Challenge Questions and/or Answers were updated successfully. </p>
Thank you,<br/>

OIM

<p>
Please do not reply to this system generated E-mail.
</p>
</body></html>

A notification event can be triggered from different places in OIM. The logic behind the triggering must be coded and plugged into OIM. This sample triggers a notification event when the challenge questions are changed using OIMClient API. The sample code to change challenge questions and trigger notification is:



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
    
 public boolean changeSecurityQuestions(String userLogin, String password, Map<String, Object> quesAns) throws Exception, UserAccountDisabledException, UserAccountInvalidException, NumberOfChallengesMismatchException, InvalidQuestionException {
 
  String oimURL = "t3://idmlab.com:14000";
  
     System.setProperty(JAVA_SECURITY_AUTH_LOGIN_CONFIG_KEY,"C:\\OIMClientTest\\config\\authwl.conf");
        System.setProperty("APPSERVER_TYPE", "wls");

        Hashtable<String, String> env = new Hashtable<String, String>();
        env.put(oracle.iam.platform.OIMClient.JAVA_NAMING_FACTORY_INITIAL, "weblogic.jndi.WLInitialContextFactory");
        env.put(oracle.iam.platform.OIMClient.JAVA_NAMING_PROVIDER_URL, oimURL);
        
        oracle.iam.platform.OIMClientoimClient = new oracle.iam.platform.OIMClient(env);
  
  
  UnauthenticatedSelfService unauthenticatedSelfService = OIMClientFactory.getUnauthenticatedSelfService(); 
  UserManager userManager = OIMClientFactory.getUserManager(); 
  NotificationService notificationService = oimClient.getService(NotificationService.class);  
  
        boolean flag = true;
  
        // changing the challenge questions for the user
        try {
            unauthenticatedSelfService.setChallengeValues(userLogin, password.toCharArray(), quesAns);
        } catch (UnauthenticatedSelfServiceException e) {
            logger.error(e.getMessage(),e);       
            Exception ex = new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("ERROR_CHANGESECURITYQUESTIONS_UNAUTHENTICATEDSELFSERVICEEXCEPTION" ); 
            throw Exception ;
        } catch (UserAccountDisabledException e) {
            logger.error(e.getMessage(),e);       
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("ERROR_CHANGESECURITYQUESTIONS_USERACCOUNTDISABLEDEXCEPTION" ); 
            throw Exception ;
        } catch (UserAccountInvalidException e) {
            logger.error(e.getMessage(),e);       
            Exception ex =new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("ERROR_CHANGESECURITYQUESTIONS_USERACCOUNTINVALIDEXCEPTION" ); 
            throw Exception ;
        } catch (NumberOfChallengesMismatchException e) {
            logger.error(e.getMessage(),e);       
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("ERROR_CHANGESECURITYQUESTIONS_NUMBEROFCHALLENGESMISMATCHEXCEPTION" ); 
            throw Exception ;
        } catch (InvalidQuestionException e) {
            logger.error(e.getMessage(),e);       
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("ERROR_CHANGESECURITYQUESTIONS_INVALIDQUESTIONEXCEPTION" ); 
            throw Exception ;
        }        
         
        try {            
            logger.debug(" Successful login using admin account inside unauth.resetPassword() using  : "+userLogin); 
            userManager = oimClient.getService(UserManager.class);
            notificationService=oimClient.getService(NotificationService.class);  
        } catch (LoginException e) {
            logger.error(" Remote login failed  inside  UnAuthen.ResetPassword() !!!!!");
            logger.error(e.getMessage(),e);               
            Exception=new Exception(e.getMessage(), e);
            Exception.setErrorMsgKey("ERROR_RESETPASSWORD_LOGINEXCEPTION");  
            throw Exception;
        }
        
  
  // Triggering a notification Event
  
  NotificationEvent eventToSend;

        try {            
            eventToSend = createNotificationEvent(userManager,"SetChallengeQuestionsTemplate",userLogin);
            logger.debug("successfully created  notification event ");
        } catch (Exception e) {
            logger.error(e.getMessage(),e);               
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("Exception while invoking createNotificationEvent() !"); 
            throw ex ; 
        }
        try {
            notService.notify(eventToSend);
            logger.debug("successfully  sent notification  ");
        } catch (EventException e) {
            logger.error(e.getMessage(),e);               
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("Exception while invoking   notify() !"); 
            throw ex ;
        } catch (UnresolvedNotificationDataException e) {
            logger.error(e.getMessage(),e);               
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("Exception while invoking   notify() !"); 
            throw ex ;
        } catch (TemplateNotFoundException e) {
            logger.error(e.getMessage(),e);               
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("Exception while invoking   notify() !"); 
            throw ex ;
        } catch (MultipleTemplateException e) {
            logger.error(e.getMessage(),e);               
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("Exception while invoking   notify() !"); 
            throw ex ;
        } catch (NotificationResolverNotFoundException e) {
            logger.error(e.getMessage(),e);               
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("Exception while invoking   notify() !"); 
            throw ex ;
        } catch (UserDetailsNotFoundException e) {
            logger.error(e.getMessage(),e);               
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("Exception while invoking   notify() !"); 
            throw ex ;
        } catch (NotificationException e) {
            logger.error(e.getMessage(),e);               
            Exception ex=new Exception(e.getMessage(), e);
            ex.setErrorMsgKey("Exception while invoking   notify() !"); 
            throw ex ;
        }
  
        logger.debug(" Updated  the qns an ans   successfully  : "  + flag);
        return flag;
    }

Friday, December 7, 2012

API to get all Enabled Security Questions in OIM

The Default getSystemChallengeQuestions() API Provided by OIMClient's UnauthenticatedSelfService will not filter out the disabled security questions. In order to get only the enabled security Questions we can use the following API.

Note: If you specify the order in which the questions have to displayed the following API will return them in the same order.



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
import Thor.API.Exceptions.tcAPIException;
import Thor.API.Exceptions.tcColumnNotFoundException;
import Thor.API.Exceptions.tcInvalidLookupException;
import Thor.API.Operations.tcLookupOperationsIntf;
import Thor.API.tcResultSet;

import java.util.HashMap;
import java.util.Map;
import java.util.Hashtable;

import oracle.iam.platform.OIMClient;

public class OIMClientTest {

 private OIMClient oimClient;
 private tcLookupOperationsIntf lookupOps = null;

    private void oimLogin(){
        try {
            System.out.println("Prototype for invoking an OIM API from a SOA Composite");
         
            String oimUserName = "xelsysadm";
            char[] oimpass = "password".toCharArray();
         
          
            String oimURL = "t3://oimhost:oimport";
            // set the initial context factory
            String oimInitialContextFactory = "weblogic.jndi.WLInitialContextFactory";
         
            // set up the environment for making the OIM API invocation
            Hashtable env = new Hashtable();
                       
            System.setProperty("APPSERVER_TYPE", "wls");
            System.setProperty("java.security.auth.login.config","C:\\config\\authwl.conf");//server or client
            
            env.put(oracle.iam.platform.OIMClient.JAVA_NAMING_FACTORY_INITIAL, oimInitialContextFactory);
            env.put(oracle.iam.platform.OIMClient.JAVA_NAMING_PROVIDER_URL, oimURL);
            env.put(oracle.iam.platform.OIMClient.APPSERVER_TYPE_WEBLOGIC, "WEBLOGIC");
            
            // get reference to OIMClient and perform login
            
            oimClient = new oracle.iam.platform.OIMClient(env);           
            oimClient.login(oimUserName, oimpass, env);        
            System.out.println("Login Successful");     
         
        } catch (Exception e) {
            e.printStackTrace();
        }  
 } 
      
    public String[] getEnabledQuestions() {
        
        lookupOps = oimClient.getService(tcLookupOperationsIntf.class);
        String[] lookUp = null;
        System.out.println("Inside getEnabledQuestions() : ");
        
        try {
            
            Map poFilters = new HashMap();
            poFilters.put("LKV_DISABLED", "0");
            tcResultSet result = lookupOps.getLookupValues("Lookup.WebClient.Questions", poFilters);  
            
            lookUp = new String[result.getTotalRowCount()];
            
            for (int i = 0; i < result.getTotalRowCount(); i++) {
                result.goToRow(i);                
                lookUp[i] = result.getStringValueFromColumn(3);               
            }
        } catch (tcAPIException e) { 
            System.out.println("tcAPIException: " +e.getMessage()); 
            e.printStackTrace();
           
        } catch (tcInvalidLookupException e) {
            System.out.println("tcInvalidLookupException: " +e.getMessage()); 
            e.printStackTrace();
            
        } catch (tcColumnNotFoundException e) {
            System.out.println("tcColumnNotFoundException: " +e.getMessage()); 
            e.printStackTrace();
          
        } catch (Exception e) {
            System.out.println("GENERIC Exception: "+e.getMessage()); 
            e.printStackTrace();
          
        }
        System.out.println("Number  of  questions returned : " + lookUp.length);   
       
        for (String key : lookUp) {
             System.out.println("Question is: " + key);
        }

        return lookUp;
    }
  
  
 public static void main(String[] args) {
  OIMClientTest test = new OIMClientTest();
 
  test.oimLogin();
  test.getEnalbedQuestions();
        
 }

}

Note: The following Jar files need to be in the class path.


Commons-logging.jar
Eclipselink.jar
Oimclient.jar
Spring.jar
Wlfullclient.jar
Jrf-api.jar

If the Eclipselink.jar from the OIMClient.zip folder is not working get the jar from the MIDDLEWARE_HOME/MiddlewareIDM/oracle_common/modules/oracle.toplink_11.1.1 folder.







Wednesday, December 5, 2012

Sample client for SPML ValidateUsernameRequest webservice

Following is a sample client class for testing ValidateUsernameRequest SPML webservice:

Pre Requisite:

Have OIMServer.jar file in your class path. OIMServer.Jar file is located at
$IDM_HOME/server/apps/oim.ear/APP-INF/lib directory.

Sample Client Class is as follows:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import com.oracle.xmlns.idm.identity.webservice.spmlservice.SPMLRequestPortType;
import com.oracle.xmlns.idm.identity.webservice.spmlservice.SPMLService;

import java.net.URL;

import java.util.ArrayList;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;

import oracle.iam.wsschema.model.spmlv2.core.ServiceHeaderType;
import oracle.iam.wsschema.model.spmlv2custom.username.ValidateUsernameResponseType;


public class SPMLWSClientTest {
 public SPMLWSClientTest() {
  super();
 }
 private static final QName SERVICE_NAME = new QName("http://xmlns.oracle.com/idm/identity/webservice/SPMLService", "SPMLService");

 public static void main(String args[]) throws Exception {

  URL wsdlURL = new URL("http://oimhost:oimport/spml-xsd/SPMLService?wsdl");
  SPMLService ss = new SPMLService(wsdlURL, SERVICE_NAME);
  SPMLRequestPortType port = ss.getSPMLServiceProviderSoap();
         
                try {
                    CustomSOAPHandler sh = new CustomSOAPHandler();
             List<Handler> new_handlerChain = new ArrayList<Handler>();
             new_handlerChain.add(sh);
             ((BindingProvider) port).getBinding().setHandlerChain(new_handlerChain);
                    //port.callService();
                } catch (Throwable e) {
                 e.printStackTrace();
                }

  ServiceHeaderType serviceHeader = new ServiceHeaderType();

  System.out.println("Invoking spmlValidateUsernameRequest...");    
                
                oracle.iam.wsschema.model.spmlv2custom.username.ValidateUsernameRequestType requestData = new oracle.iam.wsschema.model.spmlv2custom.username.ValidateUsernameRequestType();
                                
                oracle.iam.wsschema.model.spmlv2.core.ExecutionModeType async = oracle.iam.wsschema.model.spmlv2.core.ExecutionModeType.ASYNCHRONOUS;
                requestData.setExecutionMode(async);
                requestData.setLocale("en");                
                
                requestData.setUsername("test0298");        
                
                
                
                ValidateUsernameResponseType valReturn = port.spmlValidateUsernameRequest(requestData, serviceHeader);
  System.out.println("spmlValidateUsername.result= " + valReturn.getStatus());

 }

}
The output from the 

Invoking spmlValidateUsernameRequest...
spmlValidateUsername.result= SUCCESS


The CustomSoapHandler used to build the SOAP Security Header information is as follows:

Note: The username and password in the below class have to be modified to the correct values.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import java.util.Collections;
import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;


public class CustomSOAPHandler implements SOAPHandler<SOAPMessageContext> {

    private static final String AUTH_PREFIX = "wsse";
    private static final String AUTH_NS =
        "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";


    public boolean handleMessage(SOAPMessageContext context) {

        try {
            SOAPEnvelope envelope =
                context.getMessage().getSOAPPart().getEnvelope();
            SOAPFactory soapFactory = SOAPFactory.newInstance();
            SOAPElement wsSecHeaderElm =
                soapFactory.createElement("Security", AUTH_PREFIX, AUTH_NS);
            Name wsSecHdrMustUnderstandAttr =
                soapFactory.createName("mustUnderstand", "S",
                                       "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
            wsSecHeaderElm.addAttribute(wsSecHdrMustUnderstandAttr, "1");
            SOAPElement userNameTokenElm =
                soapFactory.createElement("UsernameToken", AUTH_PREFIX,
                                          AUTH_NS);
            Name userNameTokenIdName =
                soapFactory.createName("id", "wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
            userNameTokenElm.addAttribute(userNameTokenIdName,
                                          "UsernameToken-ORbTEPzNsEMDfzrI9sscVA22");
            SOAPElement userNameElm =
                soapFactory.createElement("Username", AUTH_PREFIX, AUTH_NS);
            userNameElm.addTextNode("xelsysadm");
            SOAPElement passwdElm =
                soapFactory.createElement("Password", AUTH_PREFIX, AUTH_NS);
            Name passwdTypeAttr = soapFactory.createName("Type");
            passwdElm.addAttribute(passwdTypeAttr,
                                   "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
            passwdElm.addTextNode("Password123");
            userNameTokenElm.addChildElement(userNameElm);
            userNameTokenElm.addChildElement(passwdElm);
            wsSecHeaderElm.addChildElement(userNameTokenElm);
            if (envelope.getHeader() == null) {
                SOAPHeader sh = envelope.addHeader();
                sh.addChildElement(wsSecHeaderElm);
            } else {
                SOAPHeader sh = envelope.getHeader();
                sh.addChildElement(wsSecHeaderElm);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return true;
    }

    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }

    public boolean handleFault(SOAPMessageContext context) {
        return false;
    }

    public void close(javax.xml.ws.handler.MessageContext context) {
    }
}

Monday, December 3, 2012

Creating a Custom UDF in OIM 11g R2

High level steps to Create a Custom Attribute are as follows:

1. Create Custom LDAP Attributes in OUD using ODSM.
2. Add this Attribute as Optional in OrclIDXPerson Object Class.
3. Login to OIM System Administration screen and create OIM Custom Attribute in User form and specify the Custom Attribute created in Step 1 as LDAP Attribute. (Create a sandbox and publish it after creating the Attribute)
4. Add this Attribute in the Create User, Modfiy User, and View User Details Page.
5. Create a user with adding value in the Custom Attribute and this value will be saved in OUD.

Detailed information about adding an UDF in OIM 11g R2 is available in the following document:

The below document outlines the detailed steps of Creating an Custom Object Class and Custom Attributes and Use this Custom Object Class to Create an user in OIM and OUD.