IBM Security Directory Integrator How To
save the signer cert from the browser
or
openssl s_client -showcerts -connect server.com:443
then
/opt/IBM/TDI/V7.1.1/jvm/jre/bin/keytool -import -file /userdata/server.pem -alias SerevrIssuingCA -keystore serverapi/testadmin.jks -storepass administrator
for kdb (not jks) you also need to do ../jvm/jre/bin/ikeycmd -cert -modify -db serverapi/testadmin.kdb -label serverrootca -trust enable
How to disable rmi caching
I tried many different things (see below) but only the tdi restart really worked on 7.1.1 FP 5.
You could try
/opt/ibm/identity/bin/tdiControl.sh stop;/opt/ibm/identity/bin/tdiControl.sh start
which is equivalent to /usr/bin/pkill -9 /opt/IBM/TDI/; /usr/bin/setuidgid identity /opt/IBM/TDI/V7.1.1/timsol/ITIMAd start
/opt/ibm/TDI/V7.1.1/timsol/etc/global.properties
com.ibm.di.dispatcher.disableConnectorCache=true
set /opt/ibm/TDI/V7.1.1/timsol/solution.properties
com.ibm.di.dispatcher.disableConnectorCache=true
edit /opt/ibm/TDI/V7.1.1/itim_listener.properties
set
ALCacheSize=0
AssemblylineCacheTimeout=1
A drastic measure (also did not seem to help) - disable isim search cache (als are stored in the ldap)
/opt/ibm/isim/data/enRole.properties:enrole.search.cache.enable=true
/opt/ibm/isim/data/enRole.properties:enrole.search.cache.secondary.enable=true
How to perform typecasting to force an integer as an attribute value
If you do
conn.setAttribute("INVOICE_AMT_MAX",0);
you might get "0.0" (double) as the value for the attribute, when you use it later, say for an SQL insert. Instead use the following to force type casting into a Java Interger
conn.setAttribute("INVOICE_AMT_MAX",new Packages.java.lang.Integer(0));
How to make sure TDI does not overwrite text property files
Problem: Plain text TDI property files are silently overwriten by TDI with values from a TDI XML. This happens every time TDI internal builder runs, which is typically the case right before an assembly line is executed.
Root cause: TDI assumes the .tdiproperty files associated with an AL take priority over the plain text values any time it compiles an them and the other resources from the project.
Solution: A pre-builder is added to each non-adapter TDI project. The pre-builder pushes plain text values into the TDI XML, ensuring that when the TDI builder runs it operates on the latest values from the plain text properties.
The actual code for the prebuilder is here.
How to lookup and add a value to an ou on the fly
// add the current ITIM ou into the record being written - works in conjunction with the placement rule to keep people in their current OUs
var ouEntry=ITIMOULookup.lookup((system.newEntry()).addAttributeValue("lookupemployeeid",current.getString("erparent")));
if (ouEntry != null)
conn.addAttributeValue("ou",ouEntry.getString("ou"));
How To handle results with regexp with several matches
fileName = "\n2008-12-01_1.csv\n2008-12-01_2.csv\n2008-12-01_3.csv";
myRe = "/(\d{4}-\d{2}-\d{2}_\d{1,}.csv)/g";
var a = new Array();
var x;
while( x = myRe.exec(fileName)){
a.push(x[0]);
}
var i=0
for (i=0;i<=(a.length-1);i++){
task.logmsg(a[i]);
}
How to add a value to a multivalue attribute
work.addAttributeValue("attr",value)
will create an attr if it does not exist
How to create a fresh Entry
system.newEntry()
How to decode a binary value like ADs objectGUID
Get the objectGUID attribute from AD as a binary attribute, then:
uid = work.getObject("objectGUID");
ret.value = system.binaryGUIDtoString(guid);
That will return the hex string representation of the user or group or basically anything in AD. That objectGUID is guaranteed to be unique, not only throughout nested Forests, but throughout space and time (seriously - check with Kant)
How to end a loop
system.exitBranch("LoopName");
How to exit a ITDI loop or a branch
If you want to exit a branch, Loop, or the AL Flow list, you use the system.exitBranch() method. Calling system.exitBranch() with no parameters (or with an empty string) will cause the containing Branch to exit, and flow continues with the first component after the Branch.
You can also provide the method with a string parameter containing either: One of the reserved words: "Branch", "Loop", "Flow" or "AssemblyLine" (case insensitive) This will break the first Branch of this type, tracing backwards up the AssemblyLine. So if your script code is in a Branch within a Loop, and you execute the call system.exitBranch("Loop"), you will exit both the Branch and the Loop containing it. Using the reserved work "Flow" will cause the cycle to complete, executing the Response component if required. Using the "Flow" option will cause exitBranch() to function like a call to system.skipEntry(). The "AssemblyLine" will cause the AL to stop and the Epilogs and shutdown process to begin. The name of a Branch or Loop (case sensitive) If you pass the name of a Branch or Loop in which your script call is nested, then control will pass to the component following it in the AL. If no Branch or Loop with this name is found (tracing backwards from the point of the call) then an error results.
Loops are actually cyclic Branches, and the Flow list is the main Branch of the AL (always TRUE, so it is always executed)
How to force accounts to be deleted or suspended by an IDI DSML request when a person is deleted or suspended
This will require some workflow customization in TIM. If you look at the properties of the DELETEPERSON extension node in the person delete workflow you will see that it has an "accountDelete" input parameter. When you delete a person through the web ui this value is passed into the workflow as either a true or false depending on the setting of the "Delete Accounts" toggle on the page where you submit the delete.
When you delete a person through a feed this input parameter is either not set, or is set to false. (I can't remember which) There is no way from the TDI side to set this parameter. Instead you need to customize the person delete workflow to set this parameter to true if the person delete operation is being called by an ID feed.
You can do this by adding this script to the "Start" node of the person delete workflow:
if (process.requestorType == 'P') // called by the system, not a user
accountDelete.set('true');
This will force the flag to true when the delete is from an ID feed. But deletes done from the web ui will still honor the toggle on the delete page.
You also need to be sure that your IDI feed service in ITIM has the "use workflow" toggle set. Without this all you are doing is performing LDAP operations without any of the business logic defined in the workflows.
How to get AL log config params
java.lang.System.getProperties().get("tsk").getConfigClone().getLogConfig().getItems().get(0).getParameter("information.systemlog")
java.lang.System.getProperties().get("tsk").getParent().getConfigPath()+" "+java.lang.System.getProperties().get("tsk").getLogFilePath();
How to handle errors
Put a script like this into the relevant Connector's Default On Error hook:
task.logmsg("**** Error *****");
task.dumpEntry(error);
task.logmsg("**** Work *****");
task.dumpEntry(work);
// May or may not want to do this: system.skipEntry();
The effect of this is to dump out the error object and the work object whenever an error occurs in the connector. The Java stack-trace will not be produced because the On Error hook is enabled. If you also want to dump the conn and current objects (which may not be defined at the point of the error) you can wrap these in try-catch blocks:
try {
task.logmsg("**** CONN *****");
task.dumpEntry( conn )
} catch (e) { // Do nothing }
try {
task.logmsg("**** CURRENT *****");
task.dumpEntry( current )
} catch (e) { // Do nothing }
How to limit ITDI iterator
Press the More... button for the Iterator in question and set the Read Limit. This can also be done for Feed Iterators by going to the AssemblyLine Options > AssemblyLine Settings and setting the read limit for Iterators there. Otherwise you can do it manually by doing this:
- modify the following hooks for a connector inside an assembly line
- after initialize
- get next successful
How to run manually a lookup connector
make sure it is in the AL, in passive mode if needed.
connector.lookup(entry to use for the lookup);
How to return a multi-valued attribute in ITDI
var values=new Array();
values[0]="smtp:"+work.getString("group")+"@corp.dom";
values[1]="smtp:"+work.getString("group")+"@supercorp.com";
values[2]="smtp:"+work.getString("group")+"@gw.spc.com";
values[3]="smtp:"+work.getString("group")+"@exch.sup.com";
values[4]="smtp:"+work.getString("group")+"@extra.com";
ret.value = values;
How to send email from a TDI assembly line
system.setJavaProperty("mail.smtp.host", "mail.server.com");
system.sendMail("to", "from","AL Completed Successfully","Hello", "",attchement);
need to make sure the smtp allows an open relay or simply use the SendMailFC
How to shutdown an assembly line
task.shutdown();
How to use RMI adapter interface with remote API turned on
The solution for having proper communication between ITIM and TDI while also having api.remote.on=true is to change the port specified for ITIM to connect to. Instead of specifying the TDI URL on ITIM's service form as rmi://server:16231/ITDIDispatcher, specify it as rmi://server:1099/ITDIDispatcher. In the ibmdi.log you can see the following activity with api.remote.on=true:
2007-12-20 12:31:56,537 INFO [com.ibm.di.api] - CTGDKD012I RMI Registry started on port: 1099....... 2007-12-20 12:32:03,038 INFO [D:\Program Files\IBM\TDI\V6.1.1\timsol\ITIM_RMI.xml] - startInstance():392 IDI Remote API in ON, obtaining registry from the APIEngine on port:1099 2007-12-20 12:32:03,038 WARN [D:\Program Files\IBM\TDI\V6.1.1\timsol\ITIM_RMI.xml] - startInstance():395 Ignoring property com.ibm.di.dispatcher.registryPort Since api.remote.on=true, Specified on:16231, created on:1099
Here you can see that, as a result of setting api.remote.on=true, TDI starts an RMI Registry on port 1099. Now when the Dispatcher tries to start the Registry, it finds that the RMI Registry has already been started, and reuses the one already created. So, the only change necessary to use the Dispatcher now is to specify port 1099 instead of 16231 in the ITIM service form. An additional note, for your information, is that the 1099 port number is configurable via the property api.remote.naming.port in solution.properties.
How to work with AssemblyLine Operations when starting an AL from JavaScript
Using operations from JavaScript Specifying the operation to call, as well as the Attribute values to pass into the called AL, are both done in the Task Call Block (TCB).
// Set up a new TCB and choose the "findUser" Operation.
//
var myTCB = system.newTCB();
myTCB.setOperation("findUser");
// Now I have to pass in the "uid" Input Attribute
// defined in the example screen shown earlier in this section.
// The value I want to use is in a work Entry Attribute called "userID".
//
myTCB.setOperationInitParam("uid", work.getString("userID"));
// Now I can call the AL Operation.
//
var al = main.startAL( "WS", myTCB );
al.join(); // Wait for it to complete
var resEntry = al.getResult(); // Return the resulting Attribute
A more efficient way of calling AL Operations is by using the AssemblyLine's Manual Mode feature. If you start the AL in Manual Mode, then it start all components and returns without actually doing any processing:
myTCB.setRunMode("manual");
var al = main.startAL( "WS", myTCB );
Now you can make calls to different AL Operations each time you cycle the AssemblyLine manually with the executeCycle() method.
@HowTo @ITDI