Stupid Sailpoint Developer Tricks

G’day, mates — as they say Down Under, where I happen to be at the moment on a rather large SailPoint engagement. It’s been a while, and I’m sorry for that. I keep promising more, new, and better content and haven’t delivered.

The last couple of months however have been absolutely crazy and there have been some changes on my end, as you perhaps can see. Now that things have shaped up a bit, maybe I can get back to the business at hand here on the blog, again as I have time.

Stupid Pet Tricks

When I was growing up and in college, a famous comedian became famous (partially) by having a segment on his show called “Stupid Pet Tricks.” Some were hilarious and some… belonged on the 1980’s “Gong Show.” (If you’ve never heard of “The Gong Show,” trust me, you aren’t missing anything).

Since that time, I’ve always thought of various developer tricks in the same light. Some are quite slick and useful and some… really just need to be buried. I’ll leave it to you to decide on this one.

Out of sheer laziness, while onboarding SailPoint applications that feature a BuildMap rule (eg. BuildMap, JDBCBuildMap, and SAPBuildMap), I sometimes utilize a method for “printing debug statements” that I can see directly and immediately in connectorDebug, without having to jump into or tail the SailPoint IdentityIQ log or application server logs.

It’s also just a bit less verbose as the SailPoint IdentityIQ logs typically have a large class identification prefix in front of them, which can get rather cumbersome and make it more difficult to pick out one’s intended debug output.

Plus I hate changing logging levels in log4j.properties even though the SailPoint IdentityIQ debug page allows me to load a new logging configuration dynamically. In short, I’m just a lazy, complaining type when it comes to SailPoint IdentityIQ debug statements.

Someone mentioned this would be worth blogging about, so here goes. (At the very least, this is an easy article to write and perhaps will get me back into the blogging swing?!)

__DEBUG__ Schema

Now, I would definitely recommend doing this only on a local or designated sandbox and then making sure you clean up before checking in your code. (You are using some form of source code control for your SailPoint IdentityIQ development, aren’t you?!)

This isn’t rocket science, really. The “Stupid Developer Trick” here is to:

  1. Add a __DEBUG__ attribute to the application schema you are trying to debug.
  2. Use Map.put() statements to write your info or debug statements into that place in your return map.
  3. When the ResourceObject is printed in connectorDebug, you can see your info or debug statements immediately.

Of course, none of this helps you at all if you are getting errors in aggregation that prevent you from displaying the ResourceObject in connectorDebug, but hey… I labeled this as a “Stupid Developer Trick,” so I’ve already indemnified myself (mate). 🙂

A Sample Scenario

As always, a sample scenario with accompanying code always helps. And that’s what we’re here for anyway — free code, mate.

In my sample scenario, I’ve written a small procedure (called from a library) to transform employee Ids. I just want to debug print the original employee Id and what the transformed employee Id is as a return from that procedure.

Normally this would entail a couple of log.debug() statements, which usually ends up being log.error() statements, because I don’t want to change the log levels in log4j.properties… blah, blah, blah. (And I do mean blah!!)

I just want to see the values when I run in connectorDebug. Is that too much to ask?!1

So first I’ll add the __DEBUG__ attribute to the existing application schema:

Now, I just update my BuildMap rule to enable Map.put() calls to that “attribute”:

// References:
// - Aggregation Library
//
// Imports.

import sailpoint.object.Schema;
import sailpoint.connector.Connector;
import sailpoint.connector.DelimitedFileConnector;

// Build the initial map.

HashMap map = DelimitedFileConnector.defaultBuildMap( cols, record );

if (schema.getObjectType().compareTo( Connector.TYPE_ACCOUNT ) == 0) {

   // Get/transform employee ids.

   String originalEmployeeId = map.get( "employeeId" );
   String transformedEmployeeId = getEmployeeId( originalEmployeeId );
   map.put( "__DEBUG__",
      "Original Id: " + originalEmployeeId +"   " +
      "Transformed Id: " + transformedEmployeeId
   );
   map.put( "employeeId", transformedEmployeeId );

}

// Return the map.

return map;

And now, I’ll just run connectorDebug and see my debug output directly in the resulting output:

$ cd /path/to/app-server/webapps/iiq55/WEB-INF/bin
$ ./iiq console -j
Using JLine
> connectorDebug "Financial Application" iterate
<ResourceObject displayName="JeffMurphy" identity="338" objectType="account">
  <Attributes>
    <Map>
      <entry key="__DEBUG__" value="Original Id: 1a2c3a4cx   Transformed Id: 01A2C3A4CX"/>
      <entry key="acct_lastLogin" value="04/21/2008 14:30:34"/>
      <entry key="app2_inactive" value="false"/>
      <entry key="app2_privileged" value="false"/>
      <entry key="app2_service" value="false"/>
      <entry key="dbId" value="338"/>
      <entry key="employeeId" value="01A2C3A4CX"/>
      <entry key="groupmbr">
        <value>
          <List>
            <String>AcctsPayable</String>
          </List>
        </value>
      </entry>
      <entry key="userName" value="JeffMurphy"/>
    </Map>
  </Attributes>
</ResourceObject>
   :
   :
<ResourceObject displayName="RachelStone" identity="258" objectType="account">
  <Attributes>
    <Map>
      <entry key="__DEBUG__" value="Original Id: 1c2a3b   Transformed Id: 00001C2A3B"/>
      <entry key="acct_lastLogin" value="04/17/2008 21:26:43"/>
      <entry key="app2_inactive" value="false"/>
      <entry key="app2_privileged" value="false"/>
      <entry key="app2_service" value="false"/>
      <entry key="dbId" value="258"/>
      <entry key="employeeId" value="00001C2A3B"/>
      <entry key="groupmbr">
        <value>
          <List>
            <String>AuditMgmt</String>
            <String>InternalAudit</String>
          </List>
        </value>
      </entry>
      <entry key="userName" value="RachelStone"/>
    </Map>
  </Attributes>
</ResourceObject>

And there you have it. 🙂 I can see the original employee Id and the transformed employee Id right inside of my connectorDebug output.

Going Into The Bonus Round

Okay, so “meh”… It “works.” Wanna get stupider? We can extend and augment this slightly to provide multi-lined debug statements.

First, we’ll mark the __DEBUG__ attribute in the application schema as “Multi-Valued”. This will force SailPoint IdentityIQ to expect an ArrayList for the __DEBUG__ attribute and we’ll just List.add() and create a multi-line debug slot in the resulting ResourceObject that we can write whatever we want into on “separate lines”:

And the new code to leverage the multi-valued __DEBUG__ “attribute”:

// References:
// - Aggregation Library
//
// Imports.

import sailpoint.object.Schema;
import sailpoint.connector.Connector;
import sailpoint.connector.DelimitedFileConnector;

// Build the initial map.

HashMap map = DelimitedFileConnector.defaultBuildMap( cols, record );

if (schema.getObjectType().compareTo( Connector.TYPE_ACCOUNT ) == 0) {

   // Get/transform employee ids.

   String originalEmployeeId = map.get( "employeeId" );
   String transformedEmployeeId = getEmployeeId( originalEmployeeId );

   // Completely over-the-top support for multi-lined debugging. :-)

   List debug = map.get( "__DEBUG__" );
   if (debug == null) debug = new ArrayList();
   debug.add( "Original Id: " + originalEmployeeId );
   debug.add( "Transformed Id: " + transformedEmployeeId );
   debug.add( "Stupid Developer Tricks - Will They Make Me Famous?! (Prob not... :-))" );
   map.put( "__DEBUG__", debug );

   map.put( "employeeId", transformedEmployeeId );

}

// Return the map.

return map;

And our bonus round output from connectorDebug:

> connectorDebug "Financial Application" iterate
<ResourceObject displayName="JeffMurphy" identity="338" objectType="account">
  <Attributes>
    <Map>
      <entry key="__DEBUG__">
        <value>
          <List>
            <String>Original Id: 1a2c3a4cx</String>
            <String>Transformed Id: 01A2C3A4CX</String>
            <String>Stupid Developer Tricks - Will They Make Me Famous?! (Prob not... :-))</String>
          </List>
        </value>
      </entry>
      <entry key="acct_lastLogin" value="04/21/2008 14:30:34"/>
      <entry key="app2_inactive" value="false"/>
      <entry key="app2_privileged" value="false"/>
      <entry key="app2_service" value="false"/>
      <entry key="dbId" value="338"/>
      <entry key="employeeId" value="01A2C3A4CX"/>
      <entry key="groupmbr">
        <value>
          <List>
            <String>AcctsPayable</String>
          </List>
        </value>
      </entry>
      <entry key="userName" value="JeffMurphy"/>
    </Map>
  </Attributes>
</ResourceObject>
   :
   :
<ResourceObject displayName="RachelStone" identity="258" objectType="account">
  <Attributes>
    <Map>
      <entry key="__DEBUG__">
        <value>
          <List>
            <String>Original Id: 1c2a3b</String>
            <String>Transformed Id: 00001C2A3B</String>
            <String>Stupid Developer Tricks - Will They Make Me Famous?! (Prob not... :-))</String>
          </List>
        </value>
      </entry>
      <entry key="acct_lastLogin" value="04/17/2008 21:26:43"/>
      <entry key="app2_inactive" value="false"/>
      <entry key="app2_privileged" value="false"/>
      <entry key="app2_service" value="false"/>
      <entry key="dbId" value="258"/>
      <entry key="employeeId" value="00001C2A3B"/>
      <entry key="groupmbr">
        <value>
          <List>
            <String>AuditMgmt</String>
            <String>InternalAudit</String>
          </List>
        </value>
      </entry>
      <entry key="userName" value="RachelStone"/>
    </Map>
  </Attributes>
</ResourceObject>

Clean Up and This Concludes…

Now, again (mate!)… Clean that mess up before the children see it and/or it finds its way into source code control! At least pull the __DEBUG__ attribute from the schema.

The beauty of the rest and how SailPoint IdentityIQ works — that is the code — is that you can actually leave the rest of the “debug code” in your rule and the overall rule logic will still work. (Do you know why? Because if your map contains attributes not defined in the schema… those map values get ignored when the ResourceObject is built, right?!)

And that concludes this year’s installment of “Stupid Developer Tricks.” Someone thought this was useful and encouraged me to post it. Don’t “gong” the messenger!

G’day to all you mates, from Melbourne, Australia!

——————–

1 – The situation isn’t really an issue with SailPoint IdentityIQ as much as just the way JEE development works in general, mind you.

Chris Olive

Chris Olive is a seasoned and passionate cybersecurity strategist, evangelist, consultant, trusted advisor, and hands-on technologist with over two decades of cybersecurity consulting experience in the US/UK governments, the Fortune 500, and large international companies all over the world. Chris has primary expertise in Identity Access Management and Identity Governance & Administration along with professional experience and expertise in Ethic Hacking & Penetration Testing, Secure Development, and Data Security & Encryption. Chris is a frequent writer, speaker, and evangelist on a range of cybersecurity topics. Chris is currently a Senior National Security Advisor & Architect for CDW -- a worldwide leader and innovator in solutioning, architecting, and delivering secure information technology solutions on-prem, in the cloud, multi-cloud, hybrid, or co-hosted leveraging the world's largest, best, and most trusted brands.

View all posts by Chris Olive →