Friday, October 29, 2010

Avoiding issues with referrals in LDAP searches

If the target LDAP server contains referrals, especially broken ones, this may cause issues during LDAP searches.

If you switch "is there any more objects left" test from:
while (results != null && results.hasMore())
to:
while (results != null && results.hasMoreElements())
this problem goes away.

When I have run into strange LDAP error messages I have found the LDAP Status Codes and JNDI Exceptions to be very helpful.

Wednesday, October 27, 2010

AD/LDAP reconciliation using paging

Depending on the settings on your target AD/LDAP server you may need to use pageing when doing larger searches. AD2008 per default only gives you a 1000 objects in any one LDAP search which means that most recons requires either paging or many very well defined LDAP filters that gives you a very small return sets.

Paging is supported in JNDI by activating a specific RequestControl. The LDAP server will then serve you pageSize number of records together with a cookie that you send back to get another batch. Rinse and repeat until you have all the result records.

The example code was written for TDS but can be used with any LDAP V3 compliant LDAP server.

private HashMap performTimSearch(String ouPath){
  
  NamingEnumeration results=null;
  //keyed on the String name, contains SearchResult objects
  HashMap totalResults=new HashMap(30000);  
  
  if(timAdminPassword.equals("")){   
   logger.debug("\nNo TIM admin password. No TIM search will be performed.");
   return totalResults;
  }
  try {
      // Create initial context
      LdapContext ctx = new InitialLdapContext(timEnv,null);
     
      // Activate paged results
      int pageSize = 400;
      byte[] cookie = null;
      PagedResultsControl pageControl = new PagedResultsControl(pageSize, Control.NONCRITICAL);

      //activate search controls
      SearchControls searchCtrl = new SearchControls();
      searchCtrl.setReturningAttributes(timAttributes);
      searchCtrl.setSearchScope(SearchControls.SUBTREE_SCOPE);
     
      ctx.setRequestControls(new Control[] { new PagedResultsControl(pageSize, Control.NONCRITICAL) });
   
      int total=0;
     
      // Specify the attributes to match     
      String matchAttrs = "(objectclass=user)";

      do {
          /* perform the search */
       results = ctx.search(ouPath, matchAttrs, searchCtrl);

       while (results != null && results.hasMore()) {
        SearchResult entry = (SearchResult) results.next();
        totalResults.put(entry.getName(), entry);
        total=total+1;
       }
       
          // Examine the paged results control response
          Control[] controls = ctx.getResponseControls();
          if (controls != null) {
            for (int i = 0; i < controls.length; i++) {
              if (controls[i] instanceof PagedResultsResponseControl) {
                PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i];    
                cookie = prrc.getCookie();
              }
            }
          } else {
            logger.debug("No controls were sent from the server");
          }
          // Re-activate paged results
          ctx.setRequestControls(new Control[] { new PagedResultsControl(
              pageSize, cookie, Control.CRITICAL) });

        } while (cookie != null);

     
      logger.debug("Finished TIM search. Found " + total + " users.");
     
      // Close the context when we're done
      ctx.close();
     
  } catch (Exception e) {
   logger.error("TIM search failed");
   logger.error(e.getMessage());
  }
    
     return totalResults;
 }

Monday, October 11, 2010

OIM Howto: Date based enable/disable of resources

In some cases you don't want to give a user access to a resource immediately but rather start the access at a certain point in time. In a similar fashion you may not want to give the access permanently but rather up until a certain end date.

There is a number of ways to implement this but I would recommend using the following approach:
  1. Add start date and end date to the process form.
  2. As a part of the provisioning process add a step that disables the resource if the start date hasn't happened yet or end date has passed.
  3. Create a scheduled task that checks all provisioned resources of this type and enables/disables as appropriate given the start date and end dates.
Another option is to hold the object in provisioning state until the start date hits (add a task to the provisioning process that checks the date, let the scheduled task re execute the provisioning task).

Sunday, October 3, 2010

[OIM vs TIM] Basic RBAC

In my previous post RBAC vs ABAC I talked about using your favorite provisioning tool to implement a simple case of AD group membership management. I thought it might be interesting to compare how you implement this use case in OIM and TIM.

In OIM there is a large number of different ways to do AD group memberships but lets concentrate on the most standard and out of the box method: rules, groups and access policies. In the use case that we are dealing with OIM's standard functionality actually works great. If the rules that governs which AD group is given to which group of users would be slightly different i.e. give everyone whose cost center is between 1200 - 1299 AD group X and 1300-1399 AD group Y then the OIM standard functionality basically stops working and you will have to bring an entity adapter based approach to bear (see Role based group memberships in OIM for more details on how to solve this problem)

In TIM the OIM groups are basically called Roles. The TIM role basically contains it's own OIM rule definition. The TIM role membership definition is done through an LDAP filter which of course is a much more flexible mechanism than OIM's rules. as you have access to wildcards and everything else an LDAP filter can offer you.

In TIM you use a provisioning policy to implement the mapping of TIM role (OIM group) to entitlement. A TIM provisioning policy more or less is equivalent to an Access Policy in OIM. The TIM provisioning policy contains more configuration as some of the things that is configured in pre pop adapters and the process form in OIM is controlled by the provisioning policy in TIM. The configuration in TIM is done through Javascript while you usually tend to use Java code in OIM.

To implement the use case in TIM you would:
  1. Create a TIM role for each AD group that you want to provision. 
  2. Configure each TIM role to only include the correct users using an LDAP filter.
  3. Attach a provisioning policy to each TIM role that gives the specific AD group.
  4. Done!
Overall I think that TIM is the winner in this specific battle as you can do more things without having to resort to coding in TIM. You can do the same things in OIM as in TIM but in many cases you will have to resort to coding at an earlier stage in OIM as TIM has a more flexible configuration interface.