IBM Security Identity Manager JavaScript How To

How to fill in the AD manager attribute

This technote explains how to successfully populate the erADManager attribute on a Windows AD Provisioning Policy. The concepts presented her can be used to retrieve any information from the user's manager's record.

The erADManager attribute in a Windows AD Provisioning Policy should be set to the erADDistinguishedName attribute from the Windows AD account of the Person's manager. The following examples show how this information can be retrieved:

  1. If your Person records are not using an objectclass that contains a "supervisor" attribute, then you can use the following example javascript for the Manager attribute in the provisioning policy:
{var retval = "";
var mgr = subject.getProperty("supervisor");
if (mgr != null && mgr.length > 0) {
  var accounts = mgr[0].getProperty("account");
  if (accounts != null && accounts.length > 0) {
    for (i = 0; i < accounts.length; i++) {
      if (accounts[i].getProperty("erservice")[0] == "erglobalid=0000-unque service id-00000,ou=services,erglobalid=00000000000000000000,ou=ibm,dc=com") {
         name = accounts[i].getProperty("erADDistinguishedName");
         if (name != null && name.length > 0) {
          retval = name[0];
         }
       }
     }
  }
}
return retval;}
</nowiki></pre>

In this example, we use the "supervisor" relationship to return a Person Entity based on the DN specified in the "manager" attribute on the Person record for this user. After confirming that a Person Entity was returned, we then use the "account" relationship to retrieve all of the accounts for the manager. Again, after confirming some data was returned, we iterate through each account looking for the AD account. The AD account is found by comparing the "erservice" attribute on the account with a hardcoded DN. You MUST change the DN to match the appropriate AD service in your environment. After the AD account is found, we retrieve the "erADDistinguishedName" attribute and return it.

  1. If your Person records are based on a customized objectclass that includes a "supervisor" attribute, then you will not be able to utilize the "supervisor" relationship built into ITIM. In that case, you will want to use the following javascript instead:
{var retval = "";
var mgr = subject.getProperty("manager");
if (mgr != null && mgr.length > 0) {
   var per = new Person(mgr[0]);
   var accounts = per.getProperty("account");
   if (accounts != null && accounts.length > 0) {
      for (i = 0; i < accounts.length; i++) {
          if (accounts[i].getProperty("erservice")[0] == "erglobalid=xxxxxxxxxxxxxxxxx,ou=services,erglobalid=00000000000000000000,ou=ibm,dc=com") {
             name = accounts[i].getProperty("erADDistinguishedName");
             if (name != null && name.length > 0) {
                retval = name[0];
             }
          }
      }
   }
}
return retval;}

This example is similar to the first, but with one small change. Instead of receiving back a Person Entity from the "supervisor" relationship, we retrieve the DN from the "manager" attribute and create a Person Entity based on it. From that point on, the two examples are identical. The catch here is that "new Person()" is not available in a provisioning policy by default. To enable it, you must do the following:

a) In /itim45/data/fesiextensions.properties, add the following line at the bottom: fesi.extension.ProvisioningPolicy.newPerson=com.ibm.itim.fesiextensions.PersonModelExtension b) Restart the enRole application for this change to take effect

Please keep in mind that these examples rely on working with accurate data. If your user's Person record does not have a "manager" defined, or the Person record specified in the "manager" record does not have a Windows AD account in ITIM, or the serviceDN specified in the "erservice" comparison is incorrect, then the "erADManager" attribute for the user's new Windows AD account will be blank. Furthermore, when a Windows AD account is first provisioned by ITIM, ITIM will not know about that accounts erADDistinguishedName attribute. What this means is that if you create a manager's AD account and then immediately create an employees AD account using the example above, it will not work. You MUST run a recon before the erADDistinguishedName attribute will be populated in ITIM. Also, these are just examples. They have been shown to work in default ITIM installations from 4.5.1 to 4.6, but there are no guarantees that they will work perfectly in your environment.


How to enforce group membership for a specific subset of groups

Use a provisioning policy that has a combination of default groups (that are optional to have), mandatory groups (that must be assigned) and excluded groups (to remove these that must not be assigned). The trick is to exclude ALL groups, this way the mandatory attribute will push these that are necessary, while the excluded list will remove these that are not.

Here is an example of the provisioning policy code for active directory groups that maintains distribution lists for employees, contractors, managers etc. Note the use of regular expressions on attributes:

        <ACCESS_PARAMETER attributeName="ergroup" attributeType="STRING">
            <ACCESS_PARAMETER_VALUE enforcement="DEFAULT" expressionType="SCRIPT"><![CDATA["Screen saver"]]></ACCESS_PARAMETER_VALUE>
            <ACCESS_PARAMETER_VALUE enforcement="DEFAULT" expressionType="SCRIPT"><![CDATA["Domain Users"]]></ACCESS_PARAMETER_VALUE>
            <ACCESS_PARAMETER_VALUE enforcement="EXCLUDED" expressionType="RE"><![CDATA[^(All\s|Distribution_|Exchange_Group_).*]]></ACCESS_PARAMETER_VALUE>
            <ACCESS_PARAMETER_VALUE enforcement="MANDATORY" expressionType="SCRIPT"><![CDATA[/* pick real accounts, not test*/
            if (subject.getProperty("sn")==null || subject.getProperty("sn")[0].length < 9 || subject.getProperty("sn")[0].substring(0,9).toLowerCase()!="itim-test"){
            /* use primary accounts only */
            if ((parameters.eruid[0]).equalsIgnoreCase(subject.getProperty("uid")[0])){
                /* focus on Active or Paid-Leave employees only */
                if (subject.getProperty("employeestatus") != null && subject.getProperty("employeestatus").length > 0 && (subject.getProperty("employeestatus")[0] == "A" || subject.getProperty("employeestatus")[0] == "P")) {
                    var values = new Array();
                    var i=0;
                    var suffix = " Employees"; /* assign everybody into Employees groups by default */
                    if (subject.getProperty('parent')[0].name == "Business Partners" || subject.getProperty('parent')[0].name == "Contractors")
                          suffix = " Contractors";
                    if (subject.getProperty("departmentnumber") != null && subject.getProperty("departmentnumber").length > 0)
                          values[i++] = "Distribution_"+subject.getProperty("departmentnumber")[0]+suffix;
                    if (subject.getProperty("l") != null && subject.getProperty("l").length > 0)
                          values[i++] = "All "+(subject.getProperty("l")[0]).replace(/[\\\/]/g,"-")+suffix;
                    if (subject.getProperty("managerlevel") != null && subject.getProperty("managerlevel").length > 0) {
                          if (subject.getProperty("managerlevel")[0].substring(0,1) != "S")
                            values[i++] = "All Managers Directors Officers";
                          values[i++] = "All Management";
                    }
                    /* add an employees or contractors group to everybody */
                    values[i++]="All"+ suffix;
                    /* a generic way to do this based on OU: values[i++]="All "+subject.getProperty('parent')[0].name; */
                    values[i++]="Exchange_Group_"+ subject.getProperty('uid')[0].substring(5,6);
                    return values;
                } else return null;
             } else return null;
         } else return null;]]></ACCESS_PARAMETER_VALUE>

How to generate an ITIM guid (erglobalid)

In ITIM V5.0 and earlier the erGlobalIDs can be any string of any number of alpha-numeric characters. However, for performance sake use ergloballid that could be represented as a 64 bit interger, i.e use only digits 0-9 and make it exactly 19 digits long. Here is an example in perl:

my $start=100000000;
my $end=102000000;
my $erGlobalIDPrefix="9999999999";
for(my $index=$start; $index<$end; $index++) {
   my $erGlobalID=$erGlobalIDPrefix . $index;
   ...
}

And in bash:

generate_id(){
 typeset id=''
 while [ ${#id} -lt 20 ]; do; id=$id$RANDOM; done
 awk "BEGIN { print substr(\"$id\",1,20); exit; }"
 }
generate_unique_id(){
 typeset id=$(generate_id)
 tmpfile=/tmp/srch.$$
 while ./ldapsearch $AUTH -b "$LDAP_BASEDN" -D "$LDAP_USER" -w "$LDAP_PSWD" -s sub "erglobalid=$id" dn > $tmpfile 2>&1 && [ -s $tmpfile ]; do
 id=$(generate_id)
 done
 rm $tmpfile
 print $id
 }

(from 1 and 2)

How to use random numbers in JavaScript

Here is an example of a padded random number used in an identity policy

function createIdentity(){
  var tf = false;
  var identity = "";
  var counter = 0;
  var givenname = subject.getProperty("givenname");
  if (((givenname != null) && (givenname.length > 0)))
      givenname = givenname[0];
  if(givenname == null || givenname.length == 0)
      givenname = "";
    else
      givenname = givenname.substring(0,1);
  var middlename = subject.getProperty("initials");
  if (((middlename != null) && (middlename.length > 0)))
      middlename = middlename[0];
  if(middlename == null || middlename.length == 0)
      middlename = "";
    else
      middlename = middlename.substring(0,1);
  baseidentity = givenname + middlename + subject.getProperty("sn")[0].substring(0,1)+PadDigits(Math.floor(Math.random()*1000),3);
  tf = IdentityPolicy.userIDExists(baseidentity, false, true);
  if(!tf) {
    return baseidentity;
  }
  while(tf)
  {
    counter+=1;
    identity = baseidentity + counter;
    tf = IdentityPolicy.userIDExists(identity, false, true);
  }
  return identity;
}
function PadDigits(n, totalDigits){
         n = n.toString();
         var pd = '';
         if (totalDigits > n.length) {
             for (i=0; i < (totalDigits-n.length); i++)
                 pd += '0';
         }
         return pd + n.toString();
}
return createIdentity();

The PadDigits function came from here.

How to load an external JavaScript file on the fly

Enable access to java io in the extension properties and then use the following code:

var file = new java.io.BufferedReader(new java.io.FileReader("somefolder_underTIMbase/somejavascriptfile.js" ));
var line = null;
var code = '';
while((line = file.readLine()) != null)
    code += line + '\n';
file.close();
eval(code);

How to convert password from TIM to clear text

You need to specify in Config tab of DSML v2 Event Handler the erpassword as an Extra Binary Attribute Name so IDI decodes the Base64 password. You then can convert the byte[] array to a string, and insert into the database table of your choice, or further process using MD5 libraries etc for your application.

// Convert the byte array password into cleartex
cpw = system.arrayToString(work.getObject("erpassword"));
work.setAttribute("erpassword", cpw);

How to enable access to java objects from ibmjs

In the <itim home>/data/scriptframework.properties file is the following section the describes the setup needed to get access ot the Java classes in the script.


#
# Direct Java Access Configuration
#
# To allow direct access to Java classes from scripts, add the Java classes
# or java packages that you need access to here.  The rules for adding access
# is the same as in java import statements.  You can add either a full java
# class name, or a package name ".*".  This means that "java.*" will not
# include java classes in "java.lang.*".
#
# To add access to a class or package, the key must start with
# "ITIM.java.access".  If you have multiple statements each key must be unique.
#
# This is a feature of using the IBMJS engine only.
#
# Examples:
# ITIM.java.access.lang=java.lang.*
# ITIM.java.access.obj=java.lang.Object
#


How to list all properties on a js object in ITIM

var acct=Entity.get();
var n=acct.getPropertyNames();
var z="";
for (var i=0;i<n.length;i++)
    z=z+":"+n[i];

How to mail pwd in clear text from a workflow

entity.get().getAndDecryptPassword();

and

Entity.get().setAndEncryptPassword(<string>);

works nicely. Used it & the getAnd... call on ITIM 4.6 for WinLocal and Linux services & the accounts/passwords are created correctly.

For fun (cough!) you can use the AccountSearch.searchByUidAndService(....) to find another account for a particular Person & then call the getAndDecryptPassword() which allows you to get the password from any other account (returns a string, not an array btw).

Not all references to accounts have the getAndDecryptPassword method. person.getProperty('account') you get an array of references to the person's accounts. But the references are to DirectoryObjectWrapper Java objects. These don't have the methods for password access. But you can get a reference to an AccountWrapper by using the Account constructor:

var accounts = person.getProperty('account');
for (var i = 0; i < accounts.length; i++) {
var account = new Account(accounts.dn);

How to return a multi-valued attribute from javascript in ITIM

{
 function getVals() {
   var values = new Array();
   values[0] = parameters.eruid[0];
   values[1] = 'other';
   // or you can use also push on JS Array
   values.push('other2');
   values.push(parameters.eruid[0]);
   return values;
 }
 getVals();
}

How to replace all occurences of a string using the IBM JS engine

function replaceAll(text, strA, strB) {
   sidx=0; restext="";
   while ((eidx=text.indexOf(strA,sidx)) != -1) {
      restext += text.substring(sidx,eidx)+strB;
      sidx=eidx+strA.length;
   }
   return restext+text.substring(sidx,text.length);
}

How to return a name of the orgunit for a person from java script

{subject.getProperty('parent')[0].name}

This is a special syntax, make sure to use single, not double quotes around 'parent'. 3


@HowTo @ITIM