Novell Home

Time-Triggered Actions for eDirectory

Novell Cool Solutions: Tip
By Michel Bluteau, Richard Cabana

Digg This - Slashdot This

Posted: 11 Aug 2004
 

A clock that ticks: eDirectory/DirXML time-triggered actions

Michel Bluteau, mbluteauTAKETHISOUT@novell.com
Richard Cabana, rcabanaTAKETHISOUT@novell.com

Note: To get the most from this article, you need to be familiar with the following:

  • DirXML 1.x or 2.0 (Nsure Identity Manager)
  • LoopBack driver (see documentation), CRON and LDAPMODIFY
  • Optional: XSL, Query and Command Processors (see AppNotes), or Policy Builder(NIM)
Overview and Business Needs

Nsure Identity Manager (NIM) leverages Novell eDirectory and DirXML. NIM is event-driven, which means that actions encoded in the business logic rules can only be triggered by an event. Examples might be the creation or modification of an object or an attribute, either manually in eDirectory or through a published event from a connected system. However, some business requirements may call for time-triggered actions, such as account activation/deactivation for connected systems, or daily actions, such as reports or backups. This article explains a few tricks to help you meet such business requirements.

For example, let's consider accounts that are to be activated in the future. Suppose a Human Resources payroll application publishes identities/accounts to eDirectory, assigning start dates in the future for new hires. The application would publish a new account in eDirectory like the following:

Object Class: User
Attribute cn: jsmith
Attribute Surname: Smith
Attribute loginActivationTime: "Next Monday" (in the proper time format)

Question: What will happen next Sunday night at midnight? The clock on the wall will tick, but as for eDirectory and NIM, nothing changes - no attribute will be created or modified for the user. The first idea that someone may get is to use the loginActivationTime attribute to trigger actions, but even if this attribute allows a user to login with his or her account against eDirectory (eGuide, iManager, etc) on the specified time, this occurs because of a check being performed by the login process itself, and not because some background eDirectory process updates an attribute to set the account active. As far as eDirectory and NIM are concerned, nothing happens. And since nothing happens, no action can be triggered to create the account on connected systems like Active Directory.

The same is true for the loginExpirationTime attribute. The documentation seems to indicate that when the Time is reached, the loginDisabled attribute will be changed to "true." With eDirectory 8.7.3, I was not able to confirm that this is happening. It looks like the eDirectory login process itself is doing a check at login time for this attribute. Because nothing happens when the clock ticks to the set time, and no action can be triggered to update the connected systems, we need a different approach to meet the business requirement of future-activated events.

A Timely Solution

The first thing we need is a way to tick a clock in eDirectory. As long as a clock tick is an event in eDirectory, so an attribute gets updated, business logic can kick in and trigger appropriate actions. There are probably many ways to implement a clock in eDirectory, and the trick we are about to describe is just a simple way to achieve that. It's very probably not the only one or the most effective one, but it does work (like a clock!).

Whether eDirectory and NIM are deployed on Windows, NetWare, UNIX or Linux, it's possible to use good old CRON to implement scheduled, repeating actions. On Windows, third-party CRON utilities are available, some of which are freeware. So we can leverage CRON to update an attribute in eDirectory with the current time obtained from the OS via a simple batch file. But how can we update eDirectory from a DOS or Shell script? One way is to use LDAPMODIFY, available on Linux, UNIX (native or OpenLDAP) and Windows. For NetWare, we may have to wait until NetWare 7 or Open Entreprise Server, but there may be another way to use LDAPMODIFY there.

LDAPMODIFY can call a LDIF file that implements a modification for an attribute in eDirectory. We would need to dynamically construct a LDIF file that contains the current OS time with a script - that's fairly simple to do. But what attribute and which object(s) should this LDIF file handle?

Our first idea was to extend the schema for the User class and add an attribute called currentTime. We could trigger business rules for when this attribute gets updated and work our way towards meeting our business requirements. But wait a second ... what if there were 1000, 10,000, or more users in eDirectory? What kind of LDIF file can update 10,000 objects?

Another idea: We could update a special Group object called AcctActivation, for which we have added the currentTime attribute. We could then trigger business rules that update Users who are members of this special Group, such as all Users who have been published by the Payroll application with a start date in the future (loginActivationTime attribute). For the publisher channel for the Payroll app, it's possible to set up a rule that adds the Group Membership value of AcctActivation for users with a start date in the future. So the logic goes this way:

-Every day at midnight, CRON calls a batch script that builds an LDIF file. That file updates the AcctActivation Group with the current OS time through attribute currentTime.

Here's a Linux/UNIX example:


cp /root/AccAct.seed /root/AcctAct.mod
accactseed=currentTime:
echo $accactseed $(date +%s) >> /root/AcctAct.mod
ldapmodify -h 192.168.1.54 -p 389 -x -D cn=admin,o=acme -w password -f AcctAct.mod

Where AccAct.seed is:

dn: cn=AcctActivation,ou=Special,o=acme
changetype: modify
replace: currentTime


Examples

When the currentTime attribute for our special Group AcctActivation gets updated, we need some business logic that updates all the members? currentTime attribute, or ticks their clock. Below is a XSL Stylesheet that accomplishes just that. It must be implemented in a LoopBack connector, for which the filter allows currentTime for Group on the Subscriber channel to Sync or Notify.


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:cmd="http://www.novell.com/nxsl/java/com.novell.nds.dirxml.driver.XdsCommandProcessor"
xmlns:query="http://www.novell.com/nxsl/java/com.novell.nds.dirxml.driver.XdsQueryProcessor"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="srcQueryProcessor"/>
  <xsl:param name="srcCommandProcessor"/>
  <xsl:template match="modify-attr[@attr-name='currentTime']">
    <xsl:message>We are in the stylesheet Account Activation Pulse</xsl:message>
    <xsl:variable name="currentTime" select="add-value/value"/>
    <xsl:message>Pulse Event</xsl:message>
    <!-- _________________ Query eDirectory for existing attributes __________________ -->
    <xsl:variable name="src-query">
      <query class-name="Group" dest-dn="\METAX\acme\Special\AcctActivation" scope="entry">
        <read-attr attr-name="Member"/>
      </query>
    </xsl:variable>
    <!-- _________________ Paste exiting attributes into variables __________________ -->
    <xsl:variable name="result" select="query:query($srcQueryProcessor, $src-query)"/>
    <xsl:variable name="member" select="$result//attr[@attr-name='Member']"/>
    <!-- _________________ Paste exiting attributes into variables __________________ -->
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
    <xsl:if test="$member">
      <xsl:for-each select="$member">
        <xsl:message>Updating user </xsl:message>
        <!-- ============================================================ -->
        <!-- Generate the XDS fragment that will update Users -->
        <!-- ============================================================ -->
        <xsl:variable name="add-cmd-update">
          <modify class-name="User" dest-dn="{.}">
            <modify-attr attr-name="currentTime">
              <remove-all-values/>
              <add-value>
                  <value>
                  <xsl:value-of select="$currentTime"/>
                </value>
              </add-value>
            </modify-attr>
          </modify>
        </xsl:variable>
        <!-- ============================================================ -->
        <!-- Send the XDS fragment generated above to the Src Directory -->
        <!-- ============================================================ -->
        <xsl:variable name="addResults" select="cmd:execute($srcCommandProcessor, $add-cmd-update)"/>
      </xsl:for-each>
    </xsl:if>
  </xsl:template>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

For each user for which the clock ticks (members of the AcctActivation special Group) we need to compare currentTime with loginActivationTime. If currentTime is less than loginActivationTime, we need to set the AcctStatus (schema extension, string) to "active" so the connected systems can be updated, and remove the user from the AcctActivation Group so its clock does not tick any more. For the connected systems, appropriate actions can be triggered based on a modify event for the AcctStatus attribute. The XSL Stylesheet below, implemented on a separate LoopBack driver (required in order to detect events), takes care of that:


<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:cmd="http://www.novell.com/nxsl/java/com.novell.nds.dirxml.driver.XdsCommandProcessor"
xmlns:query="http://www.novell.com/nxsl/java/com.novell.nds.dirxml.driver.XdsQueryProcessor"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="srcQueryProcessor"/>
  <xsl:param name="srcCommandProcessor"/>
  <xsl:template match="modify[@class-name='User']">
    <xsl:if test="modify-attr[@attr-name='currentTime']">
      <xsl:message>We are in the stylesheet Account Activation Monitor</xsl:message>
    <xsl:message>Pulse Event</xsl:message>
    <!-- _________________ Query eDirectory for existing attributes __________________ -->
    <xsl:variable name="src-query">
      <query class-name="User" dest-dn="{@src-dn}" scope="entry">
        <read-attr attr-name="loginActivationTime"/>
        <read-attr attr-name="currentTime"/>
      </query>
    </xsl:variable>
    <!-- _________________ Paste exiting attributes into variables __________________ -->
    <xsl:variable name="result" select="query:query($srcQueryProcessor, $src-query)"/>
    <xsl:variable name="loginActivationTime" select="$result/nds/output/instance[1]/attr[@attr-name='loginActivationTime']/value[1]"/>
    <xsl:variable name="currentTime" select="$result/nds/output/instance[1]/attr[@attr-name='currentTime']/value[1]"/>
    <!-- _________________ Paste exiting attributes into variables __________________ -->
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
    <xsl:choose>
      <xsl:when test="number($loginActivationTime) < number($currentTime)">
        <xsl:message>Updating user </xsl:message>
        <!-- ============================================================ -->
        <!-- Generate the XDS fragment that will update the User -->
        <!-- ============================================================ -->
        <xsl:variable name="add-cmd-update">
          <modify class-name="User" dest-dn="{@src-dn}">
            <modify-attr attr-name="AcctStatus">
              <remove-all-values/>
              <add-value>
                <value>
                  <xsl:value-of select="'active'"/>
                </value>
              </add-value>
            </modify-attr>
            <modify-attr attr-name="Group Membership">
              <remove-value>
                <value>
                  <xsl:value-of select="'\METAX\acme\Special\AcctActivation'"/>
                </value>
              </remove-value>
            </modify-attr>
        </modify>
        </xsl:variable>
        <!-- ============================================================ -->
        <!-- Send the XDS fragment generated above to the Src Directory -->
        <!-- ============================================================ -->
        <xsl:variable name="addResults" select="cmd:execute($srcCommandProcessor, $add-cmd-update)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>The user is not ready to be activated</xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:if>
  </xsl:template>
  <xsl:template match="node()|@*">
    <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
  </xsl:template>
</xsl:stylesheet>


Conclusion

While the example above does not cover all possible business requirements that require time- triggered events, it can be used as a starting point. XSL is used in this example instead of Policy builder, but we believe that Policy builder could be leveraged to achieve the same goal. The main idea is the use of CRON and LDAPMODIFY to tick a clock in eDirectory, with the currentTime attribute for one instance of the Group object class, AcctActivation. For each clock tick, there is an event in eDirectory, and something happens. So whatever time-based business requirements must be met, once we have a clock that ticks, we're on our way to meet the requirements with a little more work.


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© 2014 Novell