Thursday, May 20, 2010

Sharing Policies between Drivers in Novell IDM

Working with Novell IDM to integrate an HMS Payroll System hosted on an AS400 system in a DB2 database.  The customer has approximately 120 of these databases that need to be interfaced.  In order to accomplish this, I need to create 120 separate JDBC drivers.

I was able to architect and implement the drivers in such a way that all of the policies are identical.  The only differences between them are the connection parameters and some GCV's.  With all of the policies identical, I wanted to deploy them in such a way that updates to the drivers are easier than duplicating the modification 120 times.

By creating a new Library container within the driverset, I was able to reference the policies within the drivers from the shared Library.  This allows all of the drivers to subscribe to the same policy and XSL objects.  When an update needs to occur for the 120 drivers, the policy in question is updated in the library, then all of the drivers are restarted. 

To expedite the process of restarting all of the drivers, I simply restart DirXML on the servers hosting the drivers.  This will force a restart of these drivers, so long as they are set to automatic startup.

Please note that the Filter cannot be shared as it must exist on the driver object, so changes to filter will still require changing all 120 drivers individually.

Wednesday, May 19, 2010

Novell IDM CSV Fanout Driver

In a traditional Novell IDM CSV driver, you have one event, which will create one output CSV file.  You can use the output transform to format the CSV files output so it does not necessarily have to be a true CSV file, but a text file of any format you wish.

I was recently presented with a unique challenge where I needed upwards of 120 or so CSV files for a single event.  The total number of files was dependent on a few factors, but they are irrelevant as that logic was programmed in policy.  The part that was pretty nifty was creating my own version of a CSV fanout driver.

Instead of using the traditional CSV driver, I used a Null Services driver.  I used typical policy to determine my outputs, but did so in a loop, so I could loop through each iteration of an output.  Each time I hit a valid output, I constructed my text file format and held it in a local variable.  It looked something like this:


<do-set-local-variable name="lv.outputString" scope="policy">
      <arg-string>
            Construct string here
      </arg-string>
</do-set-local-variable>


I then added a ECMAScript object to my driver and added it to the driver configuration.  The ECMAScript was very simple:

importPackage(Packages.java.io);

function writeFile(fileName, contentString)
{
    try {
        var printFile = new Packages.java.io.PrintStream(fileName);
        printFile.print(contentString);
        printFile.close();
        return "";
   } catch (e) {
        return e.toString();
   }
}

All I needed to do at this point was use an XPATH expression to call my ECMAScript function.  I pass it the path to the file I want to create and the string I created.  The function will create the file with the contents I specified.

The only thing to note about this is you will need to use unique filenames.  The way I found to most easily do this is to use a timestamp.  The following file path uses a timestamp down to the millisecond, so it should always be unique.

   <do-set-local-variable name="lv.outputFile" scope="policy">
      <arg-string>
          <token-global-variable name="outputfilelocation"/>
          <token-text xml:space="preserve">\</token-text>
          <token-time format="yyyyMMddHHmmssSSS"/>
          <token-text xml:space="preserve">.csv</token-text>
      </arg-string>
   </do-set-local-variable>

Once we have the output file string, the contents string, and the ECMAScript object created and added to the driver, we just need to use our XPATH expression to call it and write out our files.

  <do-set-local-variable name="result" scope="policy">
     <arg-string>
          <token-xpath expression="es:writeFile($lv.outputFile,$lv.outputString)"/>
     </arg-string>
  </do-set-local-variable>

The local variable "result" is holding the return value, which should be blank.  If it is not blank, then there was a problem and the exception that was caught should be held in this value.  This can be used for simple error checking.

Novell IDM Fun at a Large Hospital

I’ve been working with a large hospital chain and working on solving some pretty complex challenges.  Recently, we have been working on implementing some McKesson applications within their organization, but we also wanted to implement it with the concept of ‘Roles’ kept in mind.

Let me first start with our application challenges.  The two most commonly used McKesson applications are HCI and HPP.  For HCI, we found that there is an engine underneath called cloverleaf that uses a standard protocol called HL7 to send messages around.  We have three options to explore.  The first is a proprietary HL7 driver that will connect to an HL7 listener, and pass it a carefully constructed HL7 message (the message I have to construct in the driver output transform).  The second is to use an HL7 emulator that is already in place to consume a text file in the format of that same HL7 message.  The third is to use an open API that I found to open a socket using the java api.  I can call the java api from ECMAScript and use an XPath expression within my driver to do it.  Once I open the socket, I simply pass the same HL7 message used by the other two steps through it.  Currently, I’m debugging option three, and we have already done some preliminary testing on option 2.  If we can’t get option 3 working, we can fall back on 2.

For HPP, we did something pretty nifty as well.  The customer identified an API that could be used to interface with the HPP database.  They stood up a web service that I could send calls to in a very specific format to add/modify/disable users from that system.  I simply construct a URL with a bunch of arguments on the end and call the URL.  The webserver will take the URL, process the transaction and post back a return code instead of a bunch of HTML.  Very simple implementation.  The key part was to encrypt the arguments so I wouldn’t be sending username and password combinations over the wire in clear text.  I did this by implementing an encryption protocol using ECMAScript and XPath within my driver.  So now, my URL has a bunch of seemly meaningless characters on the end, but the webserver can decode this into a meaningful set of arguments.

The next challenge for implementing these two applications came up when the customer requested to not have over 200 drivers to do this implementation.  There are over 100 locations for this customer and each location has an HCI and HPP implementation.  We needed to architect the drivers in such a way that one HCI or HPP driver could service more than one location.  Just to add a little more fun and complexity, the customer also wants the “Roles” concept to be built in as well.

I worked with the customer and we kicked ideas back and forth until we came up with what we thought would be a great implementation.  We took the idea of a ‘role’ and decided to turn it into an edirectory object.  We also decided that we may as well make each ‘facility’ its own object as well.  We already have objects for each ‘user’.  Here is how they all tie together.  For HCI, there will be a ‘role’ for each role in that system (or you can define a role in HCI as a combination of attributes that consists of access to that system).  For each facility, we will create a ‘facility’ object.  We already have a ‘user’ object for each user in their system.  So, now we have a ‘role’ object with a bunch of attributes that define what the “Nurse” role in the HCI system consists.  We also have a facility object for Hospital 123, with some attributes telling us information about that location, such as the location of its HCI system, the port number, private key file used for securely coping files over, and such.  The only information we need to store on the actual user is what role and what facility (Role – Nurse, Facility – 123).

Due to the fact that a user at this customer could potentially have access to multiple locations at the same time, we had to ensure that the attribute where we store the role and facility for HCI and HPP were multivalued.  So, we now have two attributes attached to user (well, attached to an aux class, which is attached to our users).  These two attributes are multivalued lists of facilities with roles.  We now have what access and where it should be provisioned in a list format attached to each user.

In the actual logic, we keep an eye on this attribute to know when we need to provision/deprovision/modify an account on a respective system.  I used a series of loops to go through each value and did ECMAScript functions with XPath calls to actually write out files or call URL’s.  This allowed me to service multiple sites with a single driver.  I just used a multivalued GCV on the driver to list which facilities are supported by this driver (so I could split the load if required).  When the driver determined work needed to be done for a user, I would retrieve the access information from the role object using an XPath call, then retrieve the information on where to provision this information from the facility object, tag all that information with what I need from the user (username, password, etc), then make my call.

We were able to successfully implement complex IDM systems, such as McKesson HCI and HPP, using the out-of-the-box Novell IDM components and some creative architecture.  This also gave us the ability to grant roles into the framework and service multiple sites with a single driver for each system.  The drivers can be split up if required for load balancing.   Also, if a role needs to be modified in the future, we simply need to modify the ‘role’ object in question, without having to touch the potentially thousands of users subscribing to that ‘role’.

Tuesday, April 27, 2010

Novell IDM XSL to Global Find/Replace

I heavily use Novell IDM if you haven't noticed from my other blog articles.  Recently, I needed to use Novell IDM to do a global find/replace.  Typically, this can be done in policy very quickly by just replacing all instances of a character with another character (or series of characters).

I hit an issue where the characters that I was doing the replacement with were special characters in the context of the IDM, so they were not output correctly.  In order to get the replacement done correctly, I needed to do it in the Output Transformation policy so they didn't get fubar'd while passing the text around.

So, what I did was make an XSL Template to do a global find/replace, then call the function.  I was pretty proud of my code, its pretty elegant the way it was implemented.

    <xsl:template name="globalReplace">
        <xsl:param name="outputString"/>
        <xsl:param name="target"/>
        <xsl:param name="replacement"/>
        <xsl:choose>
            <xsl:when test="contains($outputString,$target)">
                <xsl:value-of select="concat(substring-before($outputString,$target),$replacement)"/>
                <xsl:call-template name="globalReplace">
                    <xsl:with-param name="outputString" select="substring-after($outputString,$target)"/>
                    <xsl:with-param name="target" select="$target"/>
                    <xsl:with-param name="replacement" select="$replacement"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$outputString"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

So, we pass this function our information.  If the target isn't present, it just throws back the string unmodified.  If the target is present, it takes the substring up to that string, tacks on the replacement, then recursively calls itself until all replacements are done.

Nice little piece of code for XSL to hold on to.

Cisco VPN Client for 64-bit Windows 7

So, I've stumbled upon a VPN client that I can use for a 64-bit windows OS.  Someone posted this and I thought I'd pass it along because it works!  Its called Shrew VPN, frmo a company called Shrew Soft Inc.  I downloaded it for free and am using it on 64-bit Windows 7 Ultimate and it seems to be working like a champ!

http://www.shrew.net/software

Enjoy!

3DES Encrypt data with Novell IDM

If you check the blog prior to this one, I developed a method to call a web service URL from within any driver for Novell IDM.  This is all good, but what happens if we need to pass sensitive data in this URL.  We can't just do a GET operation with a URL that has a password in clear text, that's just asking for trouble.

What we do to protect this data is to throw some 3DES encryption on it before we throw the args on the end.  With the 3DES encryption, we also need a function to URLEncode the contents so the URL is written in a language the browser can send across.  Once again, I used some ECMAScript and code to get this done.

Please note, you will need to generate your own encryption key, then also use this key and similar code on the remote side to decrypt the contents to read it.

Here is the code.  The first portion is two functions.  The first function encrypts the data string passed to it, then passes it to the second function which will URLEncode it.

importPackage(Packages.javax.crypto);
importPackage(Packages.javax.crypto.spec);
importPackage(Packages.java.security.spec);
importPackage(Packages.java.io);
importPackage(Packages.sun.misc);
importClass(java.net.URLEncoder);

function DESEncrypt(theString) {
    try {
            var secretKey   = new SecretKeySpec(new Packages.sun.misc.BASE64Decoder().decodeBuffer(new java.lang.String("thisiswhereyouputyourkey")), "DESede");
            var ecipher = new Cipher.getInstance("DESede");
            ecipher.init(Cipher.ENCRYPT_MODE, secretKey);
            var utf8 = new java.lang.String(theString).getBytes("UTF8");
            var enc = ecipher.doFinal(utf8);
            return EncodeURLString(new Packages.sun.misc.BASE64Encoder().encode(enc));
        } catch (e) {
            return e.toString();
        }
       
        return null;
}

function EncodeURLString(theContents) {
    try {
        return new URLEncoder.encode(theContents, "UTF8");
    } catch (e) {
        return e.toString();
    }
    return null;
}

Once this ECMAScript is saved, pushed up, then called into your IDM, you can call it like this:

            <do-set-local-variable name="lv.EncryptedArgs" scope="policy">
                <arg-string>
                    <token-xpath expression="es:DESEncrypt($myArgs)"/>
                </arg-string>
            </do-set-local-variable>

Now, you have your encrypted arguments stored in a local variable, all nice and URL encoded.  Just tack it on the end of a URL and call it with the code defined in the previous blog article and you have now sent encrypted data across the wire.

Web Services with Novell IDM

So I have a pretty unique request.  A customer asked me to integrate IDM with a system that doesn't have a standard interface that I could use to integrate.  It did, however, have an API that we could use to create our own interface.  The customers programmer decided that the easiest way to implement this would be to setup a web service for me to call. 

While initially, I thought this was a candidate for the SOAP driver, I realized that this driver is overkill for the simplicity of this implementation.  This is a sample of how we would be creating a user in the system from IDM:

http://www.server.com/addUser?username=testuser&fname=test&lname=user&password=Passw0rd&othreattribute=other1&....

SOAP is complete overkill, I just need to construct a simlpe URL and call a linux wget on that URL.  The resulting status message would be returned instead of a typical HTML page when the API code processed the request.

I did some research and decided the easiest way to implement this would be to use ECMAScript to call some custom code that would very easily call my URL and return the result for me.  I would shove the result into a local variable in policy then jam that sucker into an attribute on the user object in eDirectory.  Seems like a very simple implementation, here is how I was able to do it.

Below is the ECMAScript for the function that I wrote.  Its a simple function, urlGet. You pass it the URL and it returns whatever is returned when a GET operation is performed on that URL.

importClass(java.net.URL);
importClass(java.io.InputStreamReader);
importClass(java.lang.StringBuilder);

function urlGet(urlString) {
try {
var url = new java.net.URL(String(urlString));
var stream = url.openStream();
var reader = new java.io.InputStreamReader(stream, "UTF-8");
var sb = new java.lang.StringBuilder();
var c;
while ((c = reader.read()) != -1) {
sb.append(String.fromCharCode(c));
}
return sb.toString();
} catch(e) {
return e.toString();
}
}

Now, we post that ECMAScript and use it in our driver with the following.  It will use the URL that is stored in the Local Variable myURL and return the results back to lv.URLResult.

            <do-set-local-variable name="lv.URLResult" scope="policy">
                <arg-string>
                    <token-xpath expression="es:urlGet($myURL)"/>
                </arg-string>
            </do-set-local-variable>

Program some logic to store the result in some sort of status attribute, then business logic to construct the correct URL and your can very easily IDM enable a web service based system.