Another example of calling Java Unique Value Finder with Driver
We no longer need to make Java calls to know what time it is.
Starting with version 3.5, there are two items to help with time.
The Time() token will get the time in most any desired format.
There is also the token-convert-time token
Name | Example | Description |
---|---|---|
!CTIME | 1261598953 | Number of seconds since Midnight, January 1, 1970. or Unix Time (Compatible with eDirectory time syntaxes) |
!JTIME | 1261598953000 | Number of Millisecond since Midnight, January 1, 1970. (Compatible with Java Time) |
!LargeInteger Date | 129060725530000000 | Number of 100-nanosecond intervals since January 1, 1601 (Compatible with Win32 LargeInteger Date) |
!FULL.TIME | 8:09:13 PM UTC | Language specific FULL time format. |
!LONG.TIME | 8:09:13 PM UTC | Language specific LONG time format. |
!MEDIUM.TIME | 8:09:13 PM | Language specific MEDIUM time format. |
!SHORT.TIME | 8:09 PM | Language specific SHORT time format. |
!FULL.DATE | Wednesday, December 23, 2009 | Language specific FULL date format. |
!LONG.DATE | December 23, 2009 | Language specific LONG date format. |
!MEDIUM.DATE | Dec 23, 2009 | Language specific MEDIUM date format. |
!SHORT.DATE | 12/23/09 | Language specific SHORT date format. |
!FULL.DATETIME | Wednesday, December 23, 2009 8:09:13 PM UTC | Language specific FULL dateTime format. |
!LONG.DATETIME | December 23, 2009 8:09:13 PM UTC | Language specific LONG dateTime format. |
!MEDIUM.DATETIME | Dec 23, 2009 8:09:13 PM | Language specific MEDIUM dateTime format. |
!SHORT.DATETIME | 12/23/09 8:09 PM | Language specific SHORT dateTime format. |
yyyy-MM-dd-HH:mm:ss Z | 2009-12-23-22:09:13 +0000 | Custom time based on Java Token Syntax![]() |
The the token-convert-time token can also do offsets from a provided time. This allows a time, like loginExpirationTime, to be set to 90 days as an example. The offset can be specified as any integer number of one of the following:
The tokens can also set to any defined time zone and can be language specific RFC 3066
See Geoffrey Carman's Article on Using the Time Tokens in IDM 3.5
This will give you a 1 digit counter after an initial collision. If you change change it if you want a counter even when no collisions, then get rid of the first pattern. If you want more than one digit, change the counter digits, and adjust the substring length in the pattern to match. (Thank you Father Ramon)
<do-set-local-variable name="proposed" scope="policy"> <arg-string> <token-substring length="8"> <token-substring length="1"> <token-attr name="Given Name"/> </token-substring> <token-substring length="1"> <token-attr name="Initials"/> </token-substring> <token-attr name="Surname"/> </token-substring> </arg-string> </do-set-local-variable> <do-set-op-dest-dn> <arg-dn> <token-unique-name counter-digits="1" counter-pattern="last" counter-use="always" name="CN" on-unavailable="error"> <arg-string> <token-local-variable name="proposed"/> </arg-string> <arg-string> <token-substring length="7"> <token-local-variable name="proposed"/> </token-substring> </arg-string> </token-unique-name> </arg-dn> </do-set-op-dest-dn>
As somewhat obvious solution, once we hit on it was using sourceName we eliminated the multiple values and the bypassed the difficulty of some clients using uniqueID.
Here is a policy to pull one value of a multi-valued attribute.
<rule> <conditions> <and> <if-operation op="equal">modify</if-operation> <if-op-attr name="Given Name" op="changing"/> </and> </conditions> <actions> <do-set-local-variable name="givenname"> <arg-node-set> <token-op-attr name="Given Name"/> </arg-node-set> <do-strip-op-attr name="Given Name"/> <do-clear-dest-attr-value name="Given Name"/> <do-for-each> <arg-node-set> <token-xpath expression="$givenname[1]"/> </arg-node-set> <arg-actions> <do-add-dest-attr-value name="Given Name"> <arg-value> <token-local-variable name="current-node"/> </arg-value> </do-add-dest-attr-value> </arg-actions> </do-for-each> </actions> </rule>
<do-set-local-variable name="runtime-instance"> <arg-object> <token-xpath expression="runtime:getRuntime()"/> </arg-object> </do-set-local-variable> <do-set-local-variable name="cmd-line"> <arg-string> <token-text xml:space="preserve">C:\WINDOWS\system32\cscript.exe</token-text> <token-text xml:space="preserve"> </token-text> <token-text xml:space="preserve">C:\scripts\Insert_Event.vbs</token-text> <token-text xml:space="preserve"> </token-text> <token-text xml:space="preserve">TIVOLI</token-text> <token-text xml:space="preserve"> </token-text> <token-attr name="arzBanknummer"/> <token-text xml:space="preserve"> </token-text> <token-attr name="arzHostId"/> <token-text xml:space="preserve"> </token-text> <token-attr name="Surname"/> <token-text xml:space="preserve"> </token-text> <token-attr name="Given Name"/> <token-text xml:space="preserve"> </token-text> <token-text xml:space="preserve">MODIFY</token-text> <token-text xml:space="preserve"> </token-text> <token-text xml:space="preserve">RESR</token-text> <token-text xml:space="preserve"> </token-text> <token-text xml:space="preserve">"Modifying user properties"</token-text> </arg-string> </do-set-local-variable> <do-trace-message> <arg-string> <token-local-variable name="cmd-line"/> </arg-string> </do-trace-message> <do-set-local-variable name="process"> <arg-object> <token-xpath expression="runtime:exec($runtime-instance, $cmd-line)"/> </arg-object> </do-set-local-variable>
<policy xmlns:runtime="http://www.novell.com/nxsl/java/java.lang.Runtime">
<do-set-local-variable name="result"> <arg-node-set> <token-xpath expression='query:search($destQueryProcessor, "subtree", "", "", "Organizational Unit", "OU", $user-locID, "L,Telephone Number")'/> </arg-node-set> </do-set-local-variable> <do-set-local-variable name="result-L"> <arg-string> <token-xpath expression='$result/attr[@attr-name="L"]/value'/> </arg-string> </do-set-local-variable> <do-set-local-variable name="result-phone"> <arg-string> <token-xpath expression='$result/attr[@attr-name="Telephone Number"]/value'/> </arg-string> </do-set-local-variable>
"I have two rules which read the value of a SINGLE-VALUE attribute, check to see if a group exists with the name of the value that was read, and create the group if necessary.
I need to do the same thing with multi-valued attributes. For example, I would read attribute "MutliValuedAttribute" which contains 3 values, "One" "Two" and "Three". Then I check to see if groups exist named "One", "Two" or "Three", and I create the groups if necessary."
And here's the response from IDM expert Father Ramon ... Solution
This is how I would do it with a single rule rather than two:
<rule> <description>Create MultiValuedAttribute groups that don't exist</description> <conditions> <and> <if-op-attr name="MultiValuedAttribute" op="available"/> </and> </conditions> <actions> <do-for-each> <arg-node-set> <token-op-attr name="MultiValuedAttribute"/> </arg-node-set> <arg-actions> <do-set-local-variable name="desiredGroup" scope="policy"> <arg-string> <token-global-variable name="group-container"/> <token-local-variable name="current-node"/> </arg-string> </do-set-local-variable> <do-set-local-variable name="desiredGroupObjectClass" scope="policy"> <arg-node-set> <token-dest-attr name="Object Class"> <arg-dn> <token-local-variable name="desiredGroup"/> </arg-dn> </token-dest-attr> </arg-node-set> </do-set-local-variable> <do-for-each> <arg-node-set> <token-xpath expression="$current-node[not($desiredGroupObjectClass = 'Group')]"/> </arg-node-set> <arg-actions> <do-add-dest-object class-name="Group"> <arg-dn> <token-local-variable name="desiredGroup"/> </arg-dn> </do-add-dest-object> </arg-actions> </do-for-each> </arg-actions> </do-for-each> </actions> </rule>
<rule> <description>Set ACLs (User: add, sync)</description> <comment xml:space="preserve">This example runs in a loopback driver.</comment> <comment name="author" xml:space="preserve">jim@willeke.com</comment> <comment name="version" xml:space="preserve">2</comment> <comment name="lastchanged" xml:space="preserve">2011-04-18</comment> <conditions> <or> <if-operation op="equal">add</if-operation> <if-operation op="equal">sync</if-operation> </or> </conditions> <actions> <do-add-dest-attr-value name="ACL"> <arg-value type="structured"> <arg-component name="protectedName"> <token-text xml:space="preserve">[Entry Rights]</token-text> </arg-component> <arg-component name="trustee"> <token-src-dn/> </arg-component> <arg-component name="privileges"> <token-text xml:space="preserve">1</token-text> </arg-component> </arg-value> </do-add-dest-attr-value> <do-add-dest-attr-value name="ACL"> <arg-value type="structured"> <arg-component name="protectedName"> <token-text xml:space="preserve">[All Attributes Rights]</token-text> </arg-component> <arg-component name="trustee"> <token-src-dn/> </arg-component> <arg-component name="privileges"> <token-text xml:space="preserve">3</token-text> </arg-component> </arg-value> </do-add-dest-attr-value> <do-add-dest-attr-value name="ACL"> <arg-value type="structured"> <arg-component name="protectedName"> <token-text xml:space="preserve">Given Name</token-text> </arg-component> <arg-component name="trustee"> <token-src-dn/> </arg-component> <arg-component name="privileges"> <token-text xml:space="preserve">7</token-text> </arg-component> </arg-value> </do-add-dest-attr-value> <do-add-dest-attr-value name="ACL"> <arg-value type="structured"> <arg-component name="protectedName"> <token-text xml:space="preserve">Surname</token-text> </arg-component> <arg-component name="trustee"> <token-src-dn/> </arg-component> <arg-component name="privileges"> <token-text xml:space="preserve">7</token-text> </arg-component> </arg-value> </do-add-dest-attr-value> <do-add-dest-attr-value name="ACL"> <arg-value type="structured"> <arg-component name="protectedName"> <token-text xml:space="preserve">Title</token-text> </arg-component> <arg-component name="trustee"> <token-src-dn/> </arg-component> <arg-component name="privileges"> <token-text xml:space="preserve">7</token-text> </arg-component> </arg-value> </do-add-dest-attr-value> <do-add-dest-attr-value name="ACL"> <arg-value type="structured"> <arg-component name="protectedName"> <token-text xml:space="preserve">Internet EMail Address</token-text> </arg-component> <arg-component name="trustee"> <token-src-dn/> </arg-component> <arg-component name="privileges"> <token-text xml:space="preserve">7</token-text> </arg-component> </arg-value> </do-add-dest-attr-value> <do-add-dest-attr-value name="ACL"> <arg-value type="structured"> <arg-component name="protectedName"> <token-text xml:space="preserve">Telephone Number</token-text> </arg-component> <arg-component name="trustee"> <token-src-dn/> </arg-component> <arg-component name="privileges"> <token-text xml:space="preserve">7</token-text> </arg-component> </arg-value> </do-add-dest-attr-value> <do-add-dest-attr-value name="ACL"> <arg-value type="structured"> <arg-component name="protectedName"> <token-text xml:space="preserve">Language</token-text> </arg-component> <arg-component name="trustee"> <token-src-dn/> </arg-component> <arg-component name="privileges"> <token-text xml:space="preserve">7</token-text> </arg-component> </arg-value> </do-add-dest-attr-value> <do-add-dest-attr-value name="ACL"> <arg-value type="structured"> <arg-component name="protectedName"> <token-text xml:space="preserve">photo</token-text> </arg-component> <arg-component name="trustee"> <token-src-dn/> </arg-component> <arg-component name="privileges"> <token-text xml:space="preserve">7</token-text> </arg-component> </arg-value> </do-add-dest-attr-value> </actions> </rule>
For those still stuck in XSL:
This is from thanks to Uffe Bager.
<xsl:template match="add[@class-name='user']"> <xsl:copy> <xsl:apply-templates select="@*"/> <add-attr attr-name="ACL"> <value type="structured"> <component name="protectedName">Prot:SSO Entry</component> <component name="trustee">\[Self]</component> <component name="privileges">15</component> </value> <value type="structured"> <component name="protectedName">Prot:SSO Auth</component> <component name="trustee">\[Self]</component> <component name="privileges">15</component> </value> <value type="structured"> <component name="protectedName">Prot:SSO Security Prefs</component> <component name="trustee">\[Self]</component> <component name="privileges">15</component> </value> <value type="structured"> <component name="protectedName">Prot:SSO Entry Checksum</component> <component name="trustee">\[Self]</component> <component name="privileges">15</component> </value> <value type="structured"> <component name="protectedName">Prot:SSO Security Checksum</component> <component name="trustee">\[Self]</component> <component name="privileges">15</component> </value> </add-attr> </xsl:copy> </xsl:template>
In this example there is a custom "locationID" attribute with a matching value on the OU. So if there was a OU with LocationID=Sales and we wanted to put all users with the values of "Sales" for a locationID on the user to the OU that possesses the same locationID attribute. Note the OUs may have more than one value for locationID.
<do-set-local-variable name="user-locID"> <arg-string> <token-src-attr name="locationID"/> </arg-string> </do-set-local-variable> <do-set-local-variable name="placement-ou-dn"> <arg-string> <token-xpath expression='query:search($destQueryProcessor, "~SearchScope~", "", "~SearchBase~", "Organizational Unit", "locationID", $user-locID, "")[1]/@src-dn'/> </arg-string> </do-set-local-variable>
Where "SearchBase" and "SearchScope" are GCVs (set SearchScope to "subtree" and leave SearchBase empty to search the whole tree). This assumes you put the policy on the publisher channel on the destination tree's driver.
<policy xmlns:date="http://www.novell.com/nxsl/java/java.util.Date" xmlns:format="http://www.novell.com/nxsl/java/java.text.SimpleDateFormat"> <rule> <description>Convert DDMMYY to seconds since 1970</description> <conditions/> <actions> <do-reformat-op-attr name="birthdate"> <arg-value type="string"> <token-xpath expression="format:format(format:new('yyyy-MM-dd'), date:new($current-value * 1000))"/> </arg-value> </do-reformat-op-attr> </actions> </rule> </policy>
<?xml version="1.0" encoding="UTF-8"?> <policy xmlns:date="http://www.novell.com/nxsl/java/java.util.Date" xmlns:format="http://www.novell.com/nxsl/java/java.text.SimpleDateFormat"> <rule> <description>Set expiration time to December 1 of gradyear</description> <conditions> <and> <if-op-attr name="Title" op="available"/> </and> </conditions> <actions> <do-set-local-variable name="DateFmt"> <arg-string> <token-text xml:space="preserve"> 12/01/ </token-text> <token-substring length="2" start="2"> <token-op-attr name="Title"/> </token-substring> </arg-string> </do-set-local-variable> <do-set-dest-attr-value name="Login Expiration Time"> <arg-value type="time"> <token-xpath expression="round(date:parse($DateFmt)div 1000)"/> </arg-value> </do-set-dest-attr-value> <do-strip-op-attr name="Title"/> </actions> </rule> </policy>
You may also be Interested in ConvCase.Java Source Code
<do-reformat-op-attr name="UPPERCASE"> <arg-value> <token-replace-all regex="\b([a-z])" replace-with="\1"> <token-lower-case> <token-local-variable name="current-value"/> </token-lower-case> </token-replace-all> </arg-value> </do-reformat-op-attr>
<do-set-local-variable name="dest-dn"> <arg-string> <token-dest-dn/> </arg-string> </do-set-local-variable> followed by: <if-local-variable name="dest-dn" op="equal" mode="regex">\uFFFC|\uFFFD</if-local-variable>
<rule> <description>User: attr L=single, if true delete from WFT/description> <conditions> <and> <if-class-name mode="nocase" op="equal">User</if-class-name> <if-association op="associated"/> <if-op-attr mode="nocase" name="L" op="equal">single</if-op-attr> </and> </conditions> <actions> <do-remove-association> <arg-association> <token-association/> </arg-association> </do-remove-association> <do-set-xml-attr name="direct" expression="../remove-association[last()]"> <arg-string> <token-text>src</token-text> </arg-string> </do-set-xml-attr> <do-delete-dest-object/> <do-veto/> </actions> </rule>
<if-src-dn op="not-in-container">~placement-location-in-active-directory~</if-src-dn>
Also of interest is the "LUM Driver"
When a new user entry was added, the driver;
<policy> <rule> <description>Get and increment counter</description> <comment name="author" xml:space="preserve">jim willeke</comment> <comment name="version" xml:space="preserve">120.02</comment> <comment name="lastchanged" xml:space="preserve">2006-02-23</comment> <conditions> <and> <if-class-name mode="nocase" op="equal">User</if-class-name> </and> </conditions> <actions> <do-set-local-variable name="lastUserIDUsed"> <arg-string> <token-dest-attr name="uamPosixUidNumberLastAssigned"> <arg-dn> <token-global-variable name="UIDNumberCounterDN"/> </arg-dn> </token-dest-attr> </arg-string> </do-set-local-variable> <do-set-local-variable name="lastUserIDUsed"> <arg-string> <token-xpath expression="$lastUserIDUsed + 1"/> </arg-string> </do-set-local-variable> <do-set-dest-attr-value direct="true" name="uamPosixUidNumberLastAssigned"> <arg-dn> <token-global-variable name="UIDNumberCounterDN"/> </arg-dn> <arg-value type="string"> <token-local-variable name="lastUserIDUsed"/> </arg-value> </do-set-dest-attr-value> <do-add-dest-attr-value name="uidNumber"> <arg-value type="string"> <token-local-variable name="lastUserIDUsed"/> </arg-value> </do-add-dest-attr-value> </actions> </rule> </policy>
This was used in a more specific policy in IDM Manage Posix Attributes
You may also be interested in our Unique Value Finder Tool.
<?xml version="1.0" encoding="UTF-8"?> <policy> <rule> <description>Query the group to see if it exists</description> <conditions> <and> <if-operation op="equal">add</if-operation> <if-attr name="xxx" op="available"/> </and> </conditions> <actions> <do-set-local-variable name="wg-dest-dn"> <arg-string> <token-dest-attr class-name="Group" name="CN"> <arg-dn> <token-attr name="xxx"/> </arg-dn> </token-dest-attr> </arg-string> </do-set-local-variable> </actions> </rule> <rule> <description>Print one trace message if the group exists</description> <conditions> <and> <if-local-variable name="wg-dest-dn" op="available"/> <if-local-variable mode="regex" name="wg-dest-dn" op="equal">.*</if-local-variable> </and> </conditions> <actions> <do-trace-message level="0"> <arg-string> <token-text xml:space="preserve" xmlns:xml="http://www.w3.org/XML/1998/namespace">The group exists</token-text> </arg-string> </do-trace-message> </actions> </rule> <rule> <description>Print one trace message if the group exists NOT</description> <conditions> <and> <if-local-variable name="wg-dest-dn" op="available"/> <if-local-variable mode="regex" name="wg-dest-dn" op="not-equal">.*</if-local-variable> </and> </conditions> <actions> <do-trace-message level="0"> <arg-string> <token-text xml:space="preserve" xmlns:xml="http://www.w3.org/XML/1998/namespace">The group doesn't exist</token-text> </arg-string> </do-trace-message> </actions> </rule> </policy>
There is also another way of doing this:
<do-if> <arg-conditions> <and> <if-xpath op=”true”>query:readObject($srcQueryProcessor,”,$lv_objectDN,”,”)</if-xpath> </and> </arg-conditions> <arg-actions> <do-set-local-variable name=”lv_objectExist” scope=”policy”> <arg-string> <token-text xml:space=”preserve”>TRUE</token-text> </arg-string> </do-set-local-variable> </arg-actions> <arg-actions> <do-set-local-variable name=”lv_objectExist” scope=”policy”> <arg-string> <token-text xml:space=”preserve”>FALSE</token-text> </arg-string> </do-set-local-variable> </arg-actions> </do-if>
If any of the attributes are missing, the "veto if operation attribute not available" action stops execution and removes the current operation.
The intent is to block object creation until the required attributes are present, thus making it a good candidate for use in the Creation policy set. It should be positioned within the policy set before any other policies referencing the required attributes.
<?xml version="1.0" encoding="UTF-8"?> <policy> <rule> <description>User required attributes: First/Last Name, Title, Description, Email</description> <conditions> <or> <if-class-name op="equal">User</if-class-name> </or> </conditions> <actions> <do-veto-if-op-attr-not-available name="Given Name"/> <do-veto-if-op-attr-not-available name="Surname"/> <do-veto-if-op-attr-not-available name="Title"/> <do-veto-if-op-attr-not-available name="Description"/> <do-veto-if-op-attr-not-available name="Internet EMail Address"/> </actions> </rule> </policy>
The conditions test for a user object and if the email attribute is changing - meaning there is an add or modify happening for that attribute.
The action sets the source value back to the value from the destination and strips out the attribute from the current operation. In this particular example, the attribute is referenced in the actions and conditions by the application schema name - a key point for policies defined in the input transformation policy set; although, this policy could be used in any policy set with some minor modifications.
The end result with this policy on the Publisher, is that the identity vault is the authoritative source for the email attribute.
NOTE: this same functionality can be applied via the Filter since the IDM 2.0.1 release.
<?xml version="1.0" encoding="UTF-8"?> <policy> <rule> <description>Push back on email changing</description> <conditions> <and> <if-class-name op="equal">User</if-class-name> <if-op-attr name="Email" op="changing"/> </and> </conditions> <actions> <do-set-src-attr-value name="Email"> <arg-value type="string"> <token-dest-attr name="Internet EMail Address"/> </arg-value> </do-set-src-attr-value> <do-strip-op-attr name="Email"/> </actions> </rule> </policy>
The first rule sets up some local variables to be used in the audit event and the trace messages.
The remaining three rules test the leading character of the surname for placement using regular expressions. In the second rule, the match expression "a-i.*" can be read as "starts with a letter between 'a' and 'i', followed by any character zero or more times". By default, regular expression matching is not case sensitive.
The first action sets the destination dn value - the main purpose for a placement policy. The remaining actions output the trace message and generate an audit event using the local variables in the first rule.
This example works specifically for the Publisher channel because the format of the destination dn is suitable for the target destination - eDirectory. This policy could be used on the Subscriber channel, but the dn format should be changed to suit the destination application.
<?xml version="1.0" encoding="UTF-8"?> <policy> <rule> <description>Setup Local Variables</description> <conditions> <and> <if-class-name op="equal">User</if-class-name> </and> </conditions> <actions> <do-set-local-variable name="LVUsers1"> <arg-string> <token-text xml:space="preserve">User:</token-text> <token-op-attr name="cn"/> <token-text xml:space="preserve"> added to the </token-text> <token-text xml:space="preserve">Training\Users\Active\Users1</token-text> <token-text xml:space="preserve"> container</token-text> </arg-string> </do-set-local-variable> <do-set-local-variable name="LVUsers2"> <arg-string> <token-text xml:space="preserve">User:</token-text> <token-op-attr name="cn"/> <token-text xml:space="preserve"> added to the </token-text> <token-text xml:space="preserve">Training\Users\Active\Users2</token-text> <token-text xml:space="preserve"> container</token-text> </arg-string> </do-set-local-variable> <do-set-local-variable name="LVUsers3"> <arg-string> <token-text xml:space="preserve">User:</token-text> <token-op-attr name="cn"/> <token-text xml:space="preserve"> added to the </token-text> <token-text xml:space="preserve">Training\Users\Active\Users3</token-text> <token-text xml:space="preserve"> container</token-text> </arg-string> </do-set-local-variable> </actions> </rule> <rule> <description>Surname A-I: place in Users1</description> <conditions> <and> <if-class-name op="equal">User</if-class-name> <if-op-attr mode="regex" name="Surname" op="equal">[a-i].*</if-op-attr> </and> </conditions> <actions> <do-set-op-dest-dn> <arg-dn> <token-text xml:space="preserve">Training\Users\Active\Users1</token-text> <token-text xml:space="preserve">\</token-text> <token-op-attr name="CN"/> </arg-dn> </do-set-op-dest-dn> <do-trace-message color="yellow"> <arg-string> <token-local-variable name="LVUsers1"/> </arg-string> </do-trace-message> <do-generate-event id="1000"> <arg-string name="text1"> <token-local-variable name="LVUsers1"/> </arg-string> </do-generate-event> </actions> </rule> <rule> <description>Surname J-R: place in Users2</description> <conditions> <and> <if-class-name op="equal">User</if-class-name> <if-op-attr mode="regex" name="Surname" op="equal">[j-r].*</if-op-attr> </and> </conditions> <actions> <do-set-op-dest-dn> <arg-dn> <token-text xml:space="preserve">Training\Users\Active\Users2</token-text> <token-text xml:space="preserve">\</token-text> <token-op-attr name="CN"/> </arg-dn> </do-set-op-dest-dn> <do-trace-message color="yellow"> <arg-string> <token-local-variable name="LVUsers2"/> </arg-string> </do-trace-message> <do-generate-event id="1000"> <arg-string name="text1"> <token-local-variable name="LVUsers2"/> </arg-string> </do-generate-event> </actions> </rule> <rule> <description>Surname S-Z: place in Users3</description> <conditions> <and> <if-class-name op="equal">User</if-class-name> <if-op-attr mode="regex" name="Surname" op="equal">[s-z].*</if-op-attr> </and> </conditions> <actions> <do-set-op-dest-dn> <arg-dn> <token-text xml:space="preserve">Training\Users\Active\Users3</token-text> <token-text xml:space="preserve">\</token-text> <token-op-attr name="CN"/> </arg-dn> </do-set-op-dest-dn> <do-trace-message color="yellow"> <arg-string> <token-local-variable name="LVUsers3"/> </arg-string> </do-trace-message> <do-generate-event id="1000"> <arg-string name="text1"> <token-local-variable name="LVUsers3"/> </arg-string> </do-generate-event> </actions> </rule> </policy>
The first condition tests if the source dn originates outside a given container, specifically a "Users" container.
The second condition tests if the LoginDisabled attribute is true.
The third condition tests if the Title attribute matches a regular expression of ".*consultant|sales.*", which reads as "any character zero or more times, followed by either the word 'consultant' or 'sales', followed by any character zero or more times". Remember that regular exprssions are case-insensitive by default.
Since the conditions are logically or'd, if any are true, the operation is vetoed. So,
<?xml version="1.0" encoding="UTF-8"?> <policy> <rule> <description>Filter events: From Users sub-tree, Users not disabled, no consultants or sales people</description> <conditions> <or> <if-src-dn op="not-in-subtree">Users</if-src-dn> <if-attr name="Login Disabled" op="equal">True</if-attr> <if-attr mode="regex" name="Title" op="equal">.*consultant|sales.*</if-attr> </or> </conditions> <actions> <do-veto/> </actions> </rule> </policy>
The conditions test for an object class of User, an operation of modify and a Description value matching the regular expression "^terminated.*". The regular expression will match a string that "starts with 'terminated' followed by any character zero or more times" (i.e. "Terminated on 02/01/2002 for insubordination") By default, regular expression matches are case-insensitive. The Description attribute is being used as the trigger to disable the user, but the conditions could easily be modified to use any other attribute or value.
The first action, to disable the account, uses "write-ahead" to immediately make the change to the Login Disabled attribute - so the user is disabled as soon as possible.
The second action, to move the user to a disabled container, happens after the current operation so the modify operation can complete before the move takes place.
The policy is written for Publisher channel use because of the use of eDirectory attributes and the DN reference to the disabled container, but the policy could be used on either channel with simple modifications.
The Command Transformation policy set is the recommended location for this policy so that it is last (on Publisher). Ordering within the policy set may be an issue, but this policy could likely be ordered anywhere.
<?xml version="1.0" encoding="UTF-8"?><policy> <rule> <description>On Termination, disable user and move to Disabled container</description> <conditions> <and> <if-operation op="equal">modify</if-operation> <if-class-name op="equal">User</if-class-name> <if-op-attr mode="regex" name="Description" op="equal">^terminated.*</if-op-attr> </and> </conditions> <actions> <do-set-dest-attr-value direct="true" name="Login Disabled"> <arg-value type="string"> <token-text xml:space="preserve">True</token-text> </arg-value> </do-set-dest-attr-value> <do-move-dest-object when="after"> <arg-dn> <token-text xml:space="preserve">Users\Disabled</token-text> </arg-dn> </do-move-dest-object> </actions> </rule> </policy>
The first step to implementing this solution involves adding the "Locked By Intruder" attribute to the filter. Do not add it to the Schema Mapping policyset, as we will not be using that type of synchronization to implement this solution. With the attribute under the User class in the filter, set the Subscriber channel synchronization to "Notify" and the Publisher channel synchronization to "Ignore". We will be retrieving the attribute's value and using it before it synchronizes to the remote system.
Add the following rules into a new policy at the top of the Subscriber Channel Command Transformation Policy Set.
This can be done in iManager by creating a new policy with Policy Builder and then pasting the following XML in the policy directly ("Edit XML").
Sample Rules
<?xml version="1.0" encoding="UTF-8"?><policy> <rule disabled="true"> <description>DisableADUser</description> <conditions> <and> <if-op-attr name="Locked By Intruder" op="changing-to">true</if-op-attr> </and> </conditions> <actions> <do-clone-op-attr dest-name="Login Disabled" src-name="Locked By Intruder"/> </actions> </rule> <rule disabled="true"> <description>PossiblyEnableADUser</description> <conditions> <and> <if-op-attr name="Locked By Intruder" op="changing"/> <if-op-attr name="Locked By Intruder" op="not-equal">true</if-op-attr> <if-src-attr name="Login Disabled" op="not-equal">true</if-src-attr> </and> </conditions> <actions> <do-add-dest-attr-value name="Login Disabled"> <arg-value type="string"> <token-text xml:space="preserve" xmlns:xml="http://www.w3.org/XML/1998/namespace">false</token-text>; </arg-value> </do-add-dest-attr-value> </actions> </rule> </policy>
<rule> <conditions> <and> <if-operation op="equal">status</if-operation> <if-xpath op="true">(@level = 'retry') and contains(., 'niuAccountLockout')</if-xpath> </and> </conditions> <actions> <do-status level="error"> <arg-string> <token-text>Whatever message you want</token-text> </arg-string> </do-status> <do-veto/> </actions> </rule>
<actions> <do-clone-xpath src-expression="$query1" dest-expression=".."/> <do-set-local-variable name="instance"> <arg-node-set> <token-xpath expression="../instance[last()]"/> </arg-node-set> </do-set-local-variable> <do-for-each> <arg-node-set> <token-xpath expression="$instance/attr[@attr-name = 'Facsimile Telephone Number']/value"/> </arg-node-set> <arg-actions> <do-set-local-variable name="faxnum"> <arg-string> <token-xpath expression="$current-node/component[@name='faxNumber']"/> </arg-string> </do-set-local-variable> <do-strip-xpath expression="$current-node/node()"/> <do-set-local-variable name="temp"> <arg-string> <token-xpath expression="$current-node/component[@name='faxNumber']"/> </arg-string> </do-set-local-variable> <do-append-xml-text expression="$current-node"> <arg-string> <token-local-variable name="faxnum"/> </arg-string> </do-append-xml-text> <do-set-xml-attr expression="$current-node" name="type"> <arg-string> <token-text>string</token-text> </arg-string> </do-set-xml-attr> </arg-actions> </do-for-each> <do-trace-message> <arg-string> <token-text xml:space="preserve">Output of Facsimile Telephone Number:</token-text> <token-xpath expression="$instance/attr[@attr-name = 'Facsimile Telephone Number']/value"/> </arg-string> </do-trace-message> <do-append-xml-element expression="$instance" name="attr"/> <do-set-xml-attr expression="$instance/attr[last()]" name="attr-name"> <arg-string> <token-text xml:space="preserve">action</token-text> </arg-string> </do-set-xml-attr> <do-append-xml-element expression="$instance/attr[last()]" name="value"/> <do-set-xml-attr expression="$instance/attr[last()]/value" name="type"> <arg-string> <token-text xml:space="preserve">string</token-text> </arg-string> </do-set-xml-attr> <do-append-xml-text expression="$instance/attr[last()]/value"> <arg-string> <token-text xml:space="preserve">D</token-text> </arg-string> </do-append-xml-text> <do-strip-xpath expression="../modify"/> <do-break/> </actions>
<?xml version="1.0" encoding="UTF-8"?><policy> <description>This policy is being used to change Delete events coming from the Person Registry, into Move events submitted to eDirectory.</description> <rule> <description>Transform deletes to move to disabled container</description> <comment xml:space="preserve">This rule looks for Delete events coming from the Person Registry. It takes those events and changes them to Move. The accounts are moved to the container ou=disabled,dc=gsu,dc=edu and the Login Disabled attribute is set to TRUE. </comment> <comment name="author" xml:space="preserve">Jeff Johnson</comment> <comment name="version" xml:space="preserve">1.0</comment> <comment name="lastchanged" xml:space="preserve">08-01-05</comment> <conditions> <and> <if-operation op="equal">delete</if-operation> </and> </conditions> <actions> <do-move-dest-object> <arg-dn> <token-text xml:space="preserve">edu\disabled</token-text> </arg-dn> </do-move-dest-object> <do-set-dest-attr-value class-name="User" name="Login Disabled"> <arg-value type="string"> <token-text xml:space="preserve">true</token-text> </arg-value> </do-set-dest-attr-value> <do-strip-xpath expression="self::delete"/> </actions> </rule> </policy>
We have found this forum to be extremely helpful with the day-to-day work of dealing with Novell's IDM product. We would list all those whom have helped, but we are sure we would leave too many people out. One solution to the problem at hand is worth any number of pages of documentation.
If Novell provided this good of support for the other products, they would be a better Company.
Special thanks to: