Sample code for moving the account to the deleted/archived container in the LDAP connector

Hi All,

In this document, I am going to talk about one use case that might come across in your project as well. The scenario is that there is an LDAP connector application. For that application, there is a use case that once the user is terminated, we have to make the user disable, delete, or archive containers. And vice versa as well, like from termination to rehire while enabling the account. Then, we don’t have the readymady feature to do that, just like we do for AC_NewParent for the AD application.

So for that, you have to write a logic to do that by using native APIs in the after provisioning rule by providing application credentials to connect with. So, basically, here I have the FreeIPA applications.

So, in the After Provisioning rule, we are checking if the request is disabled, then moving to the deleted container. And if the request is enabled, then move to the actual container of the user.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule language="beanshell" name="BPK-FreeIPA-After-Provisioning-Rule" type="AfterProvisioning">
  <Description>An IdentityIQ server-side rule that is executed after the connector's provisioning method is called. This gives the customer the ability to customize or react to anything in the ProvisioningPlan AFTER it has been sent out to the specific applications. 

    This rule will be called for any application found in a plan that also has a configured 'afterProvisioningRule' configured.</Description>
  <Signature>
    <Inputs>
      <Argument name="log" type="org.apache.commons.logging.Log">
        <Description>
          The log object associated with the SailPointContext.
        </Description>
      </Argument>
      <Argument name="context" type="sailpoint.api.SailPointContext">
        <Description>
          A sailpoint.api.SailPointContext object that can be used to query the database if necessary.
        </Description>
      </Argument>
      <Argument name="plan">
        <Description>
          The ProvisioningPlan object on its way to the Connector.
        </Description>
      </Argument>
      <Argument name="application">
        <Description>
          The application object that references this before/after script.
        </Description>
      </Argument>
      <Argument name="result">
        <Description>
          The ProvisioningResult object returned by the connectors provision method. This can be null and in many cases the connector will  not return a result and instead will annotate the plan's ProvisioningResult either at the plan or account level.        
        </Description>
      </Argument>
    </Inputs>
  </Signature>
  <Source>
  import java.util.List;
  import sailpoint.object.ProvisioningPlan.AccountRequest;
  import sailpoint.object.ProvisioningPlan.AccountRequest.Operation;
  import sailpoint.object.ProvisioningPlan.ObjectOperation;
  import sailpoint.object.Identity;
  import sailpoint.object.Application;
  import sailpoint.api.IdentityService;

  import javax.naming.Context;
  import javax.naming.NamingException;
  import javax.naming.directory.DirContext;
  import javax.naming.directory.InitialDirContext;
  import java.util.Hashtable;

  log.error("Entering into FreeIPA After Provisioning Rule");
  log.error("Plan is: "+plan.toXml());
  log.error("Result is: "+result.toXml());

  String opStatus = "";
  String dn = "";
  String uid = "";

  if(result != null)
  {
    opStatus = result.getStatus();
  }

  if(plan != null &amp;&amp; opStatus.equals("committed") &amp;&amp; null != plan.getNativeIdentity() &amp;&amp; plan.getAccountRequests("FreeIPA") != null &amp;&amp; plan.getAccountRequests("FreeIPA").size()>=1 )
  {
    List accRequests = plan.getAccountRequests("FreeIPA");
    for ( AccountRequest accReq : accRequests )
    {
      if( null != accReq.getNativeIdentity() &amp;&amp; accReq.getOp() == ObjectOperation.Disable  )
      {
        log.error("Entering Disable for moving the user to deleted container: ");
        String nativeIdentity = plan.getNativeIdentity();
        Identity identityObj = context.getObjectByName(Identity.class, nativeIdentity);

        Application application = context.getObjectByName(Application.class, "FreeIPA");
        IdentityService identityService = new IdentityService(context);
        List links = identityService.getLinks(identityObj, application);

        if(null != links &amp;&amp; links.size()>=1 )
        {
          if(null != links.get(0).getAttribute("dn") &amp;&amp; null != links.get(0).getAttribute("uid"))
          {
            dn = links.get(0).getAttribute("dn");
            uid = links.get(0).getAttribute("uid");
          }
        }

        Hashtable environment = new Hashtable();
        environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        environment.put(Context.SECURITY_AUTHENTICATION, "simple");
        environment.put(Context.REFERRAL, "follow");
        environment.put(Context.PROVIDER_URL, "ldaps://ipa-x.xxx.xxxx.xxxx.com:636");
        environment.put(Context.SECURITY_PRINCIPAL, "uid=xxx-sailpoint,cn=users,cn=accounts,dc=xxx,dc=xxx,dc=xxx");
        environment.put(Context.SECURITY_CREDENTIALS, "XXXXasdasdXXa*YAb");

        DirContext ctx = null;
        try {
          ctx = new InitialDirContext(environment);
          log.error("Context is for LDAP: "+ctx);
          String oldCn = dn;
          String newCn = "uid="+uid+",cn=deleted users,cn=accounts,cn=provisioning,dc=xxx,dc=xxx,dc=xxx";
          ctx.rename(oldCn, newCn);
          log.error("This is success: ");
        } catch (NamingException e) {
          e.printStackTrace();
        }
        finally {
          try {
            ctx.close();
          } catch (NamingException e) {
            e.printStackTrace();
          }
        }

      }
      else if( null != accReq.getNativeIdentity() &amp;&amp; accReq.getOp() == ObjectOperation.Enable )
      {
        log.error("Entering Enable for moving the user to original container: ");
        String nativeIdentity = plan.getNativeIdentity();
        Identity identityObj = context.getObjectByName(Identity.class, nativeIdentity);

        Application application = context.getObjectByName(Application.class, "FreeIPA");
        IdentityService identityService = new IdentityService(context);
        List links = identityService.getLinks(identityObj, application);

        if(null != links &amp;&amp; links.size()>=1 )
        {
          if(null != links.get(0).getAttribute("dn") &amp;&amp; null != links.get(0).getAttribute("uid"))
          {
            dn = links.get(0).getAttribute("dn");
            uid = links.get(0).getAttribute("uid");
          }
        }

        Hashtable environment = new Hashtable();
        environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        environment.put(Context.SECURITY_AUTHENTICATION, "simple");
        environment.put(Context.REFERRAL, "follow");
        environment.put(Context.PROVIDER_URL, "ldaps://ipa-x.xxx.xxxx.xxxx.com:636");
        environment.put(Context.SECURITY_PRINCIPAL, "uid=xxx-sailpoint,cn=users,cn=accounts,dc=xxx,dc=xxx,dc=xxx");
        environment.put(Context.SECURITY_CREDENTIALS, "XXXXasdasdXXa*YAb");

        DirContext ctx = null;
        try {
          ctx = new InitialDirContext(environment);
          log.error("Context is for LDAP: "+ctx);
          String oldCn = dn;
          String newCn = "uid="+uid+",cn=users,cn=accounts,dc=xxx,dc=xxx,dc=xxx";
          ctx.rename(oldCn, newCn);
          log.error("This is success: ");
        } catch (NamingException e) {
          e.printStackTrace();
        }
        finally {
          try {
            ctx.close();
          } catch (NamingException e) {
            e.printStackTrace();
          }
        }
      }
    }

  }

  log.error("Exit from FreeIPA After Provisioning Rule");
  </Source>
</Rule>

If you find this document useful, Please do a like and share it with others who need it.:slightly_smiling_face:
Happy Learning :+1::slightly_smiling_face:.

1 Like

Thanks for taking the time to make the contribution!

One thing I’d like to suggest is grabbing the account, url, ports and passwords from your application definition. That way we don’t have to store credentials in clear text. This also allows your rule to be not environment specific.

So instead of:
environment.put(Context.SECURITY_CREDENTIALS, "XXXXasdasdXXa*YAb");

using:
environment.put(Context.SECURITY_CREDENTIALS, context.decrypt(application.getAttributeValue("password")));

3 Likes

Yep, make sense. We can give a try with that as well.

Another minor nitpick: e.printStackTrace() is the equivalent of System.out.printLine() and comes with the known drawback.
you can simply do log.error(“Failed to close context”,e). That way you ensure the stack trace follows the usual logging pattern.