Quantcast
Channel: Novell User Communities - Active Directory
Viewing all 30 articles
Browse latest View live

Active Directory Services, 2008, Prevent Accidental Deletion

$
0
0

Active Directory Services, 2008, Prevent Accidental Deletion:

With the release of Microsoft Windows 2008 server new features have appeared in the operating system, and since Active Directory is currently tightly linked to the operating system, and not in any way cross platform at all, updates to Active Directory as well. Few things are as funny as listening to someone try and explain that Active Directory is cross platform since it runs on Windows 2000, 2003, and 2008 servers.

One new feature that will probably come back to bite us, from an Identity Manager perspective is a new feature of the Active Directory Users and Computers Microsoft Management Console (ADUC MMC) snapin. (Now was that a mouthful or what!) On a side note, easiest way to load it is to run dsa.msc as the executable. Just like compmgmt.msc will launch the Computer Management MMC snapin.

On Windows 2008 server, ADUC has a new option tick box (on by default), called "Protect container from accidental deletion", if you use it, it sets an Access Control Entry (ACE) for the group Everyone, to Deny access to Delete and Delete subtree. (Via special rights grants.)

You can see in this image, the new tick box item when trying to create an Organizational Unit.

I found a link to what this is doing, which you can look at:
http://blogs.technet.com/industry_insiders/pages/windows-server-2008-protection-from-accidental-deletion.aspx

Basically the option adds an Access Control Entry (ACE), what we would call in eDirectory an ACL (Access Control List). I am not sure why Microsoft decided on ACE vs ACL for the same basic attribute, but probably just a nomenclature thing. (If anyone happens to know what the subtly in the difference is, let me know, as I am curious. I suspect an ACE is a singular, and ACL is a list of ACE's but that seems sort of silly to me.) Active Directory uses a lot of Deny ACE's so they deny Delete and Delete subtree to the group Everyone, (Though the more I think about it, that may be a spec ail pseudo group, since now that I think about it, I do not recall seeing an Everyone group maintained anywhere!). Thus blocking the ability to delete it.

To fix it, you could manually remove the ACE, or if you select Advanced Options in the ADUC MMC snapin, from the View menu, see figure 2,

then when you go to Properties of the Organizational Unit object, you know will see more tabs (including Security! Which is not on by default, interestingly enough) and on the Object tab, at the bottom you can see a "Protect object from accidental deletion" tick box, see figure 3.

As it happens, although this option only seems to be part of the ADUC MMC snapin that comes with Windows Server 2008, it is supported on all versions of Active Directory, since as I just described, it is really just applying an ACE under the covers, and that is supported in earlier versions. The pretty tick box interface is what is new.

To fix this manually by hand, look at figure 4,

and lets walk through the steps.

Right click on the object (Organizational Unit in my examples) and select Properties. Then click on the Security tab. You will see a bunch of default ACE's that get applied.

Yet some people get annoyed at the Login Script and Print Job Properties default ACL entries on every object in eDirectory? Look at that list in Active Directory, I did not even show all of them in the image! For a brand new Organizational Unit I just created? Goodness!

If you do not know what I am talking about, within schema in eDirectory there are a set of default template ACL's (Access Control Lists) that all newly created objects get. For Users, that includes Read entry rights to Login Script attributes of the current object, and Read entry rights to the Print Job Configurations. I know people who have eDirectory trees that are used exclusively for LDAP that will both remove all these useless and extra ACL's from their users, and in fact modify base schema so that new users do not get them. If you want an Identity Manager toolkit rule to remove these ACL's, you can read through my toolkit rule series:

Specifically part 4, which talks about using it to look at ACL values, and then you could look at the article on using XPATH to look at DirXML-Association values, http://www.novell.com/communities/node/5845/using-...
(which use an almost identical syntax to ACL's. Technically ACL is a special syntax of its own, but its actual composition is almost identical to Path syntax, which is what DirXML-Association uses. The main difference is for Snapins to Console One and iManager to recognize this is ACL syntax and give you a different toolset for managing it. You can read more about the Schema syntaxes in eDirectory in this series of articles:

but the approach used is similar.

You could pretty easily manipulate the rules provided there to remove the two ACL's if you really wanted to get rid of them. What is nice, you could use the rule to report how many exist in your tree (every user object for sure, unless you tried to fix it) and then to clean them up afterwards. Later you could rerun it and see if more have shown up. Which is what would happen, unless you edited the Template ACL's in schema to prevent them from being applied on new objects. That is a trickier task, that can only be done via LDIF as it is considered modifying base schema, and the only way to do it is via LDIF, (or I suppose DSDump, if you call NTS and convinced them to do it for you).

Back to the topic at hand, if you are looking figure 4, you will see I have the number 1, highlighting the Everyone group, and then you would click on the Advanced button (With the #2 by it).

In the Advanced Security view, you will see that Everyone has a Special permission of Type Deny and note it is not inherited from above, and only applies to the current object (#3).

If you select the ACE for Everyone, and click the Edit button, you will see that Delete and Delete Subtree (#4) are set to Deny.

You could just untick these, or delete the ACE for the Everyone group entirely and then it goes away.

The problem is that if you are synchronizing containers between eDirectory and Active Directory, and you want to be able to synchronize deletes of containers. Now there is an entire other class of problem in doing that, because a container with contents in it, can be deleted in Active Directory, but that is not a legal operation in eDirectory, I think you get a 629 error, Object is not a Leaf object. The trick will be to read back all the contents of the container and delete the lowest nodes first and work your way up. I have to solve this problem for myself a little later, so I will write about how to do it then. For now, however, assume the simple case that via policy, all Admins in Active Directory have been told to only delete EMPTY containers.

If you do try to let the delete through, you will get the following error LDAP Insufficient Rights error, which is correct, you (as a member of everyone) do not have rights to delete this object. I wonder if removing the user the driver is connecting as, from the Everyone group would work? That would be interesting if it worked.

<nds dtdversion="1.1" ndsversion="8.7">
  <source>
    <product asn1id="" build="20080229_143300" instance="\acme-LAB-IDV\acme\Drivers\IDM\Active Directory" version="3.5.3">AD</product>
    <contact>Novell, Inc.</contact>
  </source>
  <output>
    <status event-id="Xidv1#20090313191133#1#1" level="error" type="driver-general">
      <ldap-err ldap-rc="50" ldap-rc-name="LDAP_INSUFFICIENT_RIGHTS">
        <client-err ldap-rc="50" ldap-rc-name="LDAP_INSUFFICIENT_RIGHTS">Insufficient Rights</client-err>
        <server-err>00000005: SecErr: DSID-031522EC, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0
</server-err>
        <server-err-ex win32-rc="5"/>
      </ldap-err>
    </status>
  </output>
</nds>
[03/13/09 15:11:33.485]:Active Directory ST:Applying schema mapping policies to input.
[03/13/09 15:11:33.485]:Active Directory ST:Applying policy: %+C%14C%5Bacme%5D+AD-SchemaMapping%-C.
[03/13/09 15:11:33.485]:Active Directory ST:Resolving association references.
[03/13/09 15:11:33.486]:Active Directory ST:Processing returned document.
[03/13/09 15:11:33.486]:Active Directory ST:Processing operation <status> for .
[03/13/09 15:11:33.486]:Active Directory ST:
DirXML Log Event -------------------
     Driver:   \acme-LAB-IDV\acme\Drivers\IDM\Active Directory
     Channel:  Subscriber
     Object:   \acme-LAB-IDV\acme\Structure\corp\acme\Americas\MidWest
     Status:   Error
     Message:  <ldap-err ldap-rc="50" ldap-rc-name="LDAP_INSUFFICIENT_RIGHTS">
        <client-err ldap-rc="50" ldap-rc-name="LDAP_INSUFFICIENT_RIGHTS">Insufficient Rights</client-err>
        <server-err>00000005: SecErr: DSID-031522EC, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0
</server-err>
        <server-err-ex win32-rc="5"/>
</ldap-err>

With this setting (the Protect container from Accidental Deletions) the driver cannot delete the container.

The driver shim alas, does not have access to Active Directory ACE values. This is really a shame, since it would be really useful and powerful to be able to read them, and to be able to manipulate them.

It would be useful to read them, since for another instance, I wrote a toolkit rule that looks at eDirectory ACL's and figures out users in a tree who have too many rights and reports on them. This is very useful for PCI (Payment Card Industry) audit compliance as you have to know who is a Super User in each system. With access to read the ACE's in Active Directory, it would be a lot of fun to write a similar rule for Active Directory Super Users. Having the ability to write to ACE's would be great as there all sorts of things we could do via DirXML Script then, on creating a user that would be useful. Rather than what we have to do currently, where we have to use group memberships where the ACE is set by some one some when on the group to handle rights assignments.

I opened a bug #485306 in Bugzilla, and lets see if they can do something about it. In case you are not aware, everyone can open bugs in Bugzilla now, and for most eDirectory and Identity Manager bugs, they are considered public and open for everyone to see. Obviously security hole bugs will remain private and hidden from the public until they are resolved, which is appropriate, since it would be foolish to reveal problems before Novell has had a chance to fix them. You just need to go to http://bugzilla.novell.com and login using your eLogin credentials (What you would use to download files, or open an incident with Novell) and once authenticated you can search for bugs and submit news one as appropriate.

The best solution would be for them to open up ACE's to be available to us as attributes on a user in the general case. That would be great and add a lot of power to the driver, opening up all sorts of interesting new possibilities on what can be done with the Active Directory driver.

When you use a regular LDAP tool, you cannot see the ACE values, unlike in eDirectory where you see an attribute named ACL on each user with an ACL. (And noted above, since every user gets those two default ACL values of Login Script and Print Job Configuration, EVERYBODY has an ACL to look at, regardless of how useless it might be!). However, if you use the Microsoft LDP.EXE tool that ships with Windows, (possibly the worst LDAP browser I have ever used, but that is a personal User Interface opinion, so take it with a grain of salt. Well I guess it is technically better than the ldapsearch command line utility in the sense that it actually has a user interface!) you apparently can see ACE values. Thus the data is clearly available, probably through an LDAP extension, which is the usual way of handling this. Thus I assume it should be possible to get access to the attributes. To be fair, most of the NMAS (Novell Modular Authentication Services) stuff in eDirectory needs to use an LDAP extension to get at it as well. These are the approved method of handling slightly out of specification functionality being exposed via LDAP.

This should also be resolvable via Powershell, which you could get access too via the Scripting driver, but that is an additional expense, and a bit of work to synchronize the handling of events to get the timing correct.

I imagine a tool could be written on the Active Directory side that every night would run, and remove these ACE's from any organizational unit that has them set, in the simplest approach to cleaning this mess up, but that is not particularly elegant as a solution.


How to manage multiple email addresses for users in eDirectory linked to Active Directory and Exchange with Identity Manager

$
0
0

How to use multiple e-mail addresses for users in eDirectory and Exchange.

We have users that require e-mail addresses aliases, in Microsoft Exchange terminology, proxyAddresses. This works very well to give a user a temporary e-mail address for receiving specific e-mail messages when they do not need to reply using that temporary address. For example, HR places an employment ad in the paper or online, you can give them an address specifically for that listed ad, job1234@company.com. They do not have to worry about their normal address being posted and harvested by spammers, once the ad expires, remove the proxyaddress and no more e-mail will be accepted for that address.

The problem arises that while the e-mail address field in eDirectory is mutli-valued, AD doesn't have a multi-valued field for e-mail address of a user. Because we want to be able to do our primary user maintenance in eDirectory, we needed a way to enter multiple e-mail addresses for a user and have them be transfered to Exchange 2007. Exchange also requires there to be a primary address for users, and it only differentiates that by having the address prefix SMTP: in upper case, instead of smtp: lower case, which are the proxyaddresses. Also, eDirectory has no direct designation for which value in the e-mail address field is the primary address, so it is difficult to only use this field for setting the proxyaddress values. To work around this, we created a new attribute that can be associated with the users requiring alternate addresses. IDM then uses those values as the proxyaddresses in exchange.

Note: You must already have exchange entitlement or other method of creating and linking the Exchange accounts with Identity Manager working.

Exchange: Turn off “Automatically update e-mail addresses based on e-mail address policy” for all users, or at least those who will use proxyAddresses or Exchange will reset the addresses to match the address policy.

Do these Creates on both primary eDirectory and Vault eDirectory.

create new attribute: EXProxyAddresses as Case Ignore String Sync immediate, public read.

iManager roles and tasks:

Schema: Create Attribute

name: EXProxyAddresses (leave ASN1 ID blank)

syntax: Case Ignore String

flags: Synchronize immediately, Public Read

Click Finish.

create new class: auxEXProxyAddresses with attribute as optional

iManager roles and tasks:
Schema: Create Class

name: auxEXProxyAddresses (leave ASN1 ID blank)

flags: auxiliary Class

inherit: none

mandatory: none

optional: EXProxyAddresses

naming: none

Click Finish.

Add the attribute to the Identity Manager Driver Filters:

[Do this on both Vault and eDirectory connector Driver set]

iManager Identity manager overview:
Select the eDir driver:

click the driver filter

Select the User Class:

click Add Attribute:

scroll to the bottom

click “Show all attributes"

Select the new EXProxyAddresses attribute

Set the synchronization methods:

Publish: Synchronize
Subscribe: Synchronize

Add the attribute to the Identity Manager ActiveDirectory Driver Filter:

(same screens as previous)

iManager:
  Identity manager overview:
    Select the MAD driver:
      click the driver filter
        Select the User Class:
          click Add Attribute:
            scroll to the bottom
              click “Show all attributes"
                Select the new EXProxyAddresses attribute
                  Set the synchronization methods:
                    Publish: Ignore (may choose synchronize as well)
                    Subscribe: Synchronize

Setup the proxy addresseses for AD driver- (you may need to use IE as some versions of FireFox had problems)

iManager:
  select the outputTransform from the AD driver (same place you would have the
  general setting of the exchange e-mail attribute)
    Create a Policy Rule for Primary proxy address for AD Driver [primary is SMTP:]
      Conditions:
          If class name equal "User"
          And if source attribute 'Internet EMail Address' available
      Actions:
          clear destination attribute value ("proxyAddresses",when="after")
          add destination attribute value ("proxyAddresses",when="after","SMTP:"+Source           Attribute("Internet EMail Address"))

    Create a Policy Rule for proxy E-mail addresses [these are smtp:]
      Conditions:
          if class name equal "User"
          And if source attribute 'Internet EMail Address' available
          And if source attribute 'EXProxyAddresses' available
      Actions:
          for each (nodeset(Attribute("EXProxyAddresses"))
          actions (add destination attribute           value ("proxyAddresses",when="after","smtp:"+Local Variable("current-node"))))

Add the class selected user objects:

iManager:
  Roles and tasks:
    Schema:
      Object Extentions
        Enter object: (can only do one at a time)
          current list: [if auxEXProxyAddresses is not listed, click Add]
            select auxEXProxyAddresses from the list and click ok.

iManager: alternative
  ViewObjects:
    Browse tab:
      Select the user: Select Object Extensions
        click OK to extend the selected object
          current list: [if auxEXProxyAddresses is not listed, click Add]
            select auxEXProxyAddresses from the list and click ok.

ConsoleOne:
  Select user to add the class to
    right click: select Extensions of this object
      Add Extension: auxEXProxyAddresses
        (click ok, to no template)
          (enter anything for name (even space))

Add proxy addresses to the User object:

iManager:
  ViewObjects: Browse
    select the user: Modify object (not modify user)
      Other tab: double click EXProxyAddress
        click the + to add [- to remove, pencil to edit]
          add/remove/edit the proxy addresses
          (do NOT include the primary e-mail address here)
          click OK or apply to add them

ConsoleOne:
  Select user to add proxy addresses to (double click or right click: properties)
      Other tab:
        if EXProxyAddresses is not listed, click Add and select it)
          add values [ to add another, click EXProxyAddress and click Add]
          (do NOT include the primary e-mail address here)
          click OK or apply to save the changes.

IDM will then synchronize your EXProxyAddress entries to your Exchange ProxyAddress values in Active Directory.

Configuring Novell SecureLogin LDAP to Change eDirectory and Active Directory Users' Passwords

$
0
0

Author: Dinesh PV

This article explains how to disable the password policies set for Active Directory and eDirectory. After you have disabled the password polices, you can configure Novell SecureLogin to change Active Directory and eDirectory users' passwords, simultaneously.

Table of Contents:

1. Introduction
2. Prerequisite
3. Disabling Active Directory Password Policy Settings
4. Disabling eDirectory Password Policy Settings
5. Changing Active Directory and eDirectory User Password
     5.1Configuring Change Password Resource List to Select Both the Directories
6. Conclusion

1. Introduction

With Novell SecureLogin 7.0 installed in LDAPGINA mode, you can change the passwords of eDirectory and Active Directory users at the same time.

To use this functionality, you must specify Active Directory and eDirectory passwords as per the password policy setting or disable the password policies for both the directories.

If you attempt to change the user password for both Active Directory and eDirectory when the password policies for both the directories are enabled, one of the following happens:

  • Password policies of both the directories take effect, which makes the password policy either complex or invalid.
  • Password change might fail for one of the directory because of a mismatch of the password policy.

The procedures explained in the document apply to:

  • Novell SecureLogin 7.0 or later.
  • Microsoft Windows 2003 server with Active Directory and eDirectory server 8.8 SP4.

2. Prerequisite

  • Novell SecureLogin must be installed in eDirectory LDAP GINA mode on workstation connected to Active Directory domain.

3. Disabling Active Directory Password Policy Settings

By default, Active Directory password policy is enabled.

To disable:

  1. Click Start > Programs > Administrative Tools > Domain Security Policy.
  2. From the left pane, select Security Settings > Account Policies > Password Policy.
  3. Change the Password Policy settings with the following values:
    PolicyValue
    Enforce password history 0
    Maximum password age 0
    Minimum password age 0
    Minimum password length 0
    Password must meet complexity requirements Disabled
    Store password using reversible encryptionDisabled
  4. After you have set the value for a policy, click OK.
  5. Exit the administrative tool.
  6. Restart the Group Policy by running the gpupdate /force command from the command prompt.

4. Disabling eDirectory Password Policy Settings

By default, eDirectory password policy is disabled. If it is enabled, do the following to disable it.

  1. Login to iManager as eDirectory administrator user.
  2. From Roles and Tasks select Password > Password Policies.
  3. Click the configured password policy, then the Policy Assignment tab.
  4. From the Assign to list, select and remove the user or container object to which the password policy is applied.
  5. Click Apply to save your changes.
  6. Click OK to exit.

5. Changing Active Directory and eDirectory User Password

  1. Login to Novell SecureLogin in LDAP GINA mode.
  2. Press Ctr+Alt+Delete, then select Change Password.
  3. From the Change Password Resource list, select both Active Directory and eDirectory domain.
  4. Specify the old password and new password.
  5. Click OK.
    Both Active Directory and eDirectory user passwords are changed at the same time.

5.1 Configuring Change Password Resource List to Select Both the Directories

To configure the Change Password Resource list to always select both Active Directory and eDirectory domains, create a registry key and set the value.

  1. Click Start > Run, then type regedit.
  2. Browse to HKEY_LOCAL_MACHINE\SOFTWARE\Novell\Login\LDAP.
  3. Create a DWORD registry key named DisableCADUserSelection.
  4. Set the value of the registry key to 1.

6. Conclusion

Disabling Active Directory and eDirectory password policies synchronizes the password of both the directories after every LDAP password change operation.

Dynamic Local User Problem on Active Directory Workstations

$
0
0

Been a while, I have been made aware of an issue with DLU in AD environments causing problems, especially so with new PCs in the Active Directory. Perhaps I'm not searching properly so perhaps this fix is documented elsewhere, but I found a solution. Hardly a fix, more following a convention.

We use in some environments DLU. This is to allow users to login with a controlled desktop and to access secure Citrix apps external to our own environment.

I came across a problem where DLU would not work - I was under the opinion it does. In a meeting a colleague said that DLU was always a problem on Active Directory workstations.

Really? I didn't seem to think so. After a bit of debate I attempted to prove him wrong.

In typical fashion I failed miserably.

However I did succeed - here's how.

When you try to login with the DLU account, it stops at the next login screen.

I discovered that a specific DLU account we use for unlocking PCs was fine. Why was this, was there something different?

I was rather annoyed as I had got this working in the past.

I noticed a lot of people have been grumbling on the internet about this. I saw one suggestion of creating local accounts on every PC, which is a bit excessive and a lot of work to manage.

In my environment there is a transition to ensure that complex passwords are used. The Active Directory has the settings already. An argument is where AD is not used, complex passwords are still used - makes perfect sense to me?

I discovered that when I created a test account and bolted on a DLU it did not work. You don't need the equivalent account creating in the AD. But this creates the clue. If you create an AD account it will work and create the DLU - why?

This is total nonsense, you don't need the Active Directory account. What became apparent was that both accounts have a complex password. In reality if you attempt to login with an eDirectory account onto a Active Directory PC with DLU, if the Password does not conform to the Active Directory password policy, it won't work.

I created a new account eDirectory account, set password as something simple - fails.

I reset the accounts eDirectory password to a more complicated one - works.

I created a new account, set password to a more complicated one - works.

The account with the DLU we use to unlock accounts has guess what - a complicated password. Irrespective if there is an Active Directory account, if a user move has a normal account with AD, and moves to a context that has DLU in place, the login will bypass AD entirely.

In a nutshell if your entire organisation has Active Directory but there is a need in some cases to have DLU, provided your NDS passwords conform to whatever standard is set in your AD, it will work every time on every Active Directory PC.

It makes sense as one department we have all the users created with DLU, but when created we do so with complicated passwords. Our helpdesk when enabling the accounts, advise the users the prudence of using complicated passwords. Something must be working otherwise they would not be able to get in.

Hope this helps someone.

Tony

Quick & Dirty AD Cache Inspector for AD Driver for Novell Identity Manager

$
0
0

Sometimes there is a need for us (Consultants) to see a snapshot of all the changes that happened on the Active directory side while the Novell AD IDM driver was stopped or was not running, before we decide to start the AD driver.

Since Novell Identity Manager currently allows us to see all the events which happened in the Identity vault only, but not on the AD side, I decided to write such a tool myself, and of course wanted to share this tool with the consultants/community out there. It's a .NET 2.0 WinForm application, written in C# programming language.

To run this tool you should have at minimum:

  • .NET 2.0 framework installed, ( Not supported on the Linux platforms yet)
  • This application must be run under the same user which is configured on the AD driver.

Short Tutorial (How To):

When you run the application (ADCView.exe), the application automatically discovers the current domain, a domain controller, and default domain naming context in the user logged in domain automatically.

The application will automatically set the user security context as the current logged on domain user ( Use current credentials). Or alternatively you can define credentials under which this application will run. I suggest to run this application under the same user which has been configured on the AD driver.

The application takes input from a driver state file "DRIVER_DN__state.xml", which can be found on the server, location(s) depends on the driver configuration setup (remote loader) or without remote loader. (Read the Novell IDM documentation for more details..)

And that's it!, Just click "Run", and you will see the events in a text file that can be opened either in notepad.exe or from within this application (View/Cache Log).

Terms of Use of Applications:

You understand and agree that you install and/or use all applications at your own risk.

AttachmentSize
ADCView.zip60.08 KB

Generic Single-valued Schema Enforcement

$
0
0

It is a frequent problem for AD and many other drivers that the schema of the target application does not match the schema of eDirectory. In Active Directory in particular, the use of multi-valued attributes, while supported, tends to be the exception rather than the rule. eDirectory tends in the opposite direction.

A major challenge of AD drivers is that AD's behavior when an attribute does not meet the schema requirements is that it acts much like a database– it treats a directory update like an atomic database operation. If anything is wrong, everything is rejected. For that reason, often users complain that a percentage of their users did not sync.

The most common example is usually the "Description" attribute, which in eDirectory is multi-valued, and people often fill it with notes about what changes they have manually made to eDirectory objects. By default this is mapped to description in AD, but this is a single-valued attribute. So every user who has more than one value in Description in eDirectory fails to sync anything to AD. And often, 1 out of 250 users are in this state.

Nothing is more frustrating to the end client than what appears to be inconsistent and unpredictable behavior. Most of the reactions I hear to this are similar to, "I guess IDM isn't really ready for primetime". Even when we as directory consultants identify the root cause, they insist that the system "just work".

Now we could play whack-a-mole with these attributes, fixing them one at a time, which is what I used to do. But with IDM 3.5 and some of the new tokens it became possible to read the driver's settings and take appropriate actions, which is what this policy does.

The requirement is that you actually import the schema from the application for this to work properly. The AD driver delivered functionality has an early AD schema built-in, but if the client is using a later "functional level" of the domain or forest, or if they have extended the schema with Microsoft or third party directory enabled applications, those schema extensions should be imported using the schema policy's "Import Application Schema" capability. NOTE: Not all drivers support the <query-schema> command, most notably, eDirectory drivers do not.

The schema gets stored as an XML document in the attribute DirXML-ApplicationSchema on the driver object. This rule will read that into a driver scoped node-set variable (since it is static data) if it is not already populated; this means it should only be read in once.

This rule should sit in the Output Transformation so it can operate in the application name space after the schema map policy has been applied. The rule will iterate through each attribute that is in the current command being sent to the connected system and retrieve the attribute name. It then looks up whether that attribute is single or multi-valued from the application schema; if the attribute is single valued, the first value being sent to Active Directory will be saved in a variable, the value stripped and replaced with only the single value.

The rule works as follows:

  1. Optional: Read the application schema from the driver set if it is not already there
  2. Iterate through the command document, for each attr-name you find:
    1. Get the attribute definition from the attribute schema
    2. Get the value of the multi-valued flag from the attribute
    3. If the attribute is single-valued
      1. Get the first value using the operation attribute token
      2. Strip all the values
      3. Set the value of the attribute using the set destination attribute token

The cool thing here is that we read the application schema only once, after that we operate on the node set using XPATH to decide what to do, making this a very efficient memory only operation.

It is important to note that in the AD schema, some attribute names are associated with inherited classes; for example, you might inherit telephoneNumber from person and the fact that it is OK for a user to have a telephoneNumber is implied because user is defined as inheriting from person. This means you have to find the attribute wherever it is in the application schema document, not just underneath the class.

<rule>
	<description>[CIS] Handle Multi-to-single valued conversions</description>
	<comment xml:space="preserve">Generic Rule which reads the application schema from AD and determines if it needs to take only the first value from a multi-valued eDirectory attribute
</comment>
	<conditions>
		<or>
			<if-operation mode="case" op="equal">modify</if-operation>
			<if-operation mode="case" op="equal">add</if-operation>
		</or>
	</conditions>
	<actions>
		<do-if>
			<arg-conditions>
				<and>
					<if-local-variable name="APP-SCHEMA" op="not-available"/>
				</and>
			</arg-conditions>
			<arg-actions>
				<do-set-local-variable name="APP-SCHEMA" scope="driver">
					<arg-node-set>
						<token-xml-parse notrace="true">
							<token-base64-decode notrace="true">
								<token-src-attr name="DirXML-ApplicationSchema" notrace="true">
									<arg-dn>
										<token-global-variable name="dirxml.auto.driverdn"/>
									</arg-dn>
								</token-src-attr>
							</token-base64-decode>
						</token-xml-parse>
					</arg-node-set>
				</do-set-local-variable>
			</arg-actions>
			<arg-actions/>
		</do-if>
		<do-for-each>
			<arg-node-set>
				<token-xpath expression=".//@attr-name"/>
			</arg-node-set>
			<arg-actions>
				<do-set-local-variable name="CLASS" scope="policy">
					<arg-string>
						<token-class-name/>
					</arg-string>
				</do-set-local-variable>
				<do-set-local-variable name="ATTR-DEF" notrace="true" scope="policy">
					<arg-node-set>
						<token-xpath expression="$APP-SCHEMA/schema-def/class-def/attr-def[@attr-name=$current-node]"/>
					</arg-node-set>
				</do-set-local-variable>
				<do-set-local-variable name="MULTI-VALUED" scope="policy">
					<arg-string>
						<token-xpath expression="$ATTR-DEF[1]/@multi-valued"/>
					</arg-string>
				</do-set-local-variable>
				<do-if>
					<arg-conditions>
						<and>
							<if-local-variable mode="nocase" name="MULTI-VALUED" op="equal">false</if-local-variable>
						</and>
					</arg-conditions>
					<arg-actions>
						<do-set-local-variable name="VALUE" scope="policy">
							<arg-string>
								<token-op-attr name="$current-node$"/>
							</arg-string>
						</do-set-local-variable>
						<do-strip-op-attr name="$current-node$"/>
						<do-set-dest-attr-value name="$current-node$">
							<arg-value>
								<token-local-variable name="VALUE"/>
							</arg-value>
						</do-set-dest-attr-value>
					</arg-actions>
					<arg-actions/>
				</do-if>
			</arg-actions>
	</actions>
</rule>

How to manage and synchronize GroupWise and eDirectory accounts with Active Directory

$
0
0

Migrating from NetWare to Windows Server but still want to keep GroupWise?

Running Windows for authentication, file and print services and GroupWise for collaboration?

Riva Identity Integration from Omni enables administrators to use Microsoft Management Console and Active Directory to manage eDirectory and GroupWise. When a new account is created in Active Directory, Riva automatically creates the associated eDirectory and GroupWise account. Changes to account passwords in Active Directory automatically get updated in GroupWise and eDirectory.

Riva Identity Integration takes 15 minutes to install and configure. There are no Active Directory or eDirectory schema extensions required, no complex designer tools, connectors or scripting languages to learn and no knowledge of internal LDAP or directory attributes is required.

Sign up for the upcoming GroupWise for Active Directory presentation on Wednesday, February 24th at 3:00 p.m. Eastern:

www.omni-ts.com/omni-web-seminars.html

More information:

Go to www.omni-ts.com/newsroom/groupwise-for-active-directory.html or contact Omni at 780-423-4200 or sales@omni-ts.com.

dsbedit

$
0
0
download url: 
http://www.champwilde.f9.co.uk/idmphp/dsbedit/dsbedit.zip
license: 
University of Illinois/NCSA Open Source
home page url: 
http://www.champwilde.f9.co.uk/idmphp/dsbedit/index.html

These scripts were written for an eDirectory implementation, and some features will only be useful on that platform.

Dsbedit.php
The "home directory" feature will only be useful on Novell/eDirectory. Otherwise this script should be useful on any LDAP platform. We use it principally in conjunction with data moves from server to server, and so the home directory functionality is important. It also provides a simple and easy to use means of manipulating LDAP directories without the complexities of more powerful and sophisticated tools.
To use it a csv file containing two columns, one the cn attribute and the other A N other LDAP attribute is placed on the web server, and the application matches unique CNs and modifies the other attribute with the provided value. Duplicated CNs are skipped.

Dsgrpcrt.php
This script will probably only be useful on Novell/eDirectory as written, but could be modified for other platforms. Its designed to help with large organisation changes. A csv file contains a list of groups and usernames against each group. On running it the groups are created unless they already exist, and users are made members of the groups. Both the groupMembership and securityEquals attributes are populated for each user, and the member and equivalentToMe attributes are populated for each group. The script handles all the four attributes that must be changed to make eDirectory group membershp fully functional.

Adgrpcrt.php
This script is very similar to dsgrpcrt above, but its intended
to work with MS AD groups and memberships, and will probably only be of
use with AD. The sAMAccountName attribute (pre Windows 2000 name) is
used to match objects, and the CN of the object can be set separately
from the source text file. In accordance with AD practice the group
membership is set only on the group object using the member attribute.


Using ktab to generate a Kerberos Ticket File without spn.exe or ktpass.exe

$
0
0

The Novell Documentation clearly describes how to set up Kerberos for Access Manager, but it does not take into account when the iDP is running on Windows and that server is a member server of the domain. spn.exe requires the User ID, which is the server name according to the documentation, but this is already taken by the Computer object in Active Directory.

The following describes an alternative way of setting the servicePrincipalName on the user object and generating the nidpkey.keytab file.

The assumption for this article is that a 2008 Domain Controller exists in the domain (supported as of Access Manager 3.1 SP2).

In this example, the 2008 Active Directory Domain is domain.com, the Kerberos Rhelm is DOMAIN.COM, the Domain Controller is srvdc, the iDP server name is srvidp, and the iDP URL is idp.domain.com. The workstation performing these actions only needs to be able to communicate with AD and the KDC, it does not have to be a member of the domain.

  1. Create the user srvidp in Active Directory with the userPrincipalName of HTTP/idp.domain.com@DOMAIN.COM, the pre-Windows 2000 username of srvidp, and the desired password (in this example it is idpuserpassword).
  2. Using the Windows Server 2008 Active Directory Users and Computers console, enable Advanced Features under the View menu.
  3. Under the properties of the srvidp user, select the Attribute Editor tab.
  4. Double click the servicePrincipalName attribute to edit.
  5. Add HTTP/idp.domain.com and HTTP/idp.domain.com@DOMAIN.COM.
  6. Install Sun JRE SE and make the bin directory part of the path (if this worksation is part of the domain, the klist executable in this same directory can also list issued Kerberos Tickets to the domain workstation and logged in domain user).
  7. Create a C:\WINDOWS\krb5.ini file with the following content:
  8. [libdefaults]
        default_realm = DOMAIN.COM
        default_tkt_enctypes = rc4-hmac des3-cbc-sha1 des-cbc-crc des-cbc-md5
        default_tgs_enctypes = rc4-hmac des3-cbc-sha1 des-cbc-crc des-cbc-md5
        permitted_enctypes = rc4-hmac des3-cbc-sha1 des-cbc-crc des-cbc-md5
    
    [realms]
        DOMAIN.COM = {
            kdc = srvdc.domain.com
            admin_server = srvdc.domain.com
            default_domain = domain.com
        }
     
    [domain_realms]
        domain.com = DOMAIN.COM
        .domain.com = DOMAIN.COM
  9. From a command prompt, execute
  10. ktab -a HTTP/idp.domain.com idpuserpassword -k nidpkey.keytab
  11. Validation of the content is made by executing the following command which should output something similar to the below output.
ktab -l -k nidpkey.keytab
Keytab name: C:\nidpkey.keytab
KVNO    Principal
---------------------------------------------------------
  1     HTTP/idp.domain.com@DOMAIN.COM
  1     HTTP/idp.domain.com@DOMAIN.COM
  1     HTTP/idp.domain.com@DOMAIN.COM
  1     HTTP/idp.domain.com@DOMAIN.COM

Each line represents a key for the desired encryption as defined in the krb5 file (rc4-hmac, des3-cdc-sha1, etc).

Oooh, my own first personal TID: 7006039

GroupLink Announces a New Improved Website

$
0
0

GroupLink® would like to invite you to visit our new website for everything HelpDesk®, the Novell Integrated help desk.

To see this site, visit:

http://www.grouplink.net/products/ehd.html

Some features of this new site are:

- New! online demo of everything HelpDesk
- Novell integration with the help desk
- Video Testimonials
- The first ZENworks 10 Integrated help desk
http://www.grouplink.net/products/ehd/zen10.html

We hope you enjoy the new information presented. And remember, this powerful 100% web help desk solution features key integration with your Novell environment. Some of these integrations include:

- GroupWise (and Outlook/Exhange) email and calendaring integration
- Asset management with ZENworks 10 Integration
- Directory Integration with eDirectory (and Active Directory)

No More AD Chaos: Novell File Management Suite Brings Even Greater Control to File Storage in Active Directory Environments

$
0
0

No More AD Chaos: Novell File Management Suite Brings Even Greater Control to File Storage in Active Directory Environments
By Ken Baker, Novell Connection Magazine - September 2010

Here's an excerpt:

Novell recently released an update to Novell File Management Suite, an offering that combines Novell expertise in file storage and identity technologies to deliver intelligent file management. With integration between Active Directory and the following three Novell products, the suite works together to provision, relocate, optimize and report on file storage based on user roles and customized business policies:

  • Novell Storage Manager
  • Novell File Reporter
  • Novell Dynamic File Services

Policy Power

The major update to Novell File Management Suite is that its Novell Storage Manager component now delivers feature parity between Active Directory and Novell eDirectory environments. The significant enhancements in this new version of Novell Storage Manager bring to Active Directory environments the same powerful file-level management capabilities that used to be available only in eDirectory environments. In short, there is no longer a requirement for any Novell infrastructure to enjoy the full benefits of Novell File Management Suite.

Novell Storage Manager ties together the directory, identity and file systems, enabling organizations to align user storage resources with corporate policy. The product accomplishes this by dynamically managing and provisioning storage based on user and group events that occur in either Novell eDirectory or Microsoft Active Directory.

Prior to this update, Novell Storage Manager could only manage file storage in a user's home folder in Active Directory environments. Now it has the ability to manage almost every aspect of storage as it relates to Active Directory users and groups, including auxiliary or collaborative storage (discrete pieces of storage associated with a user or group).

Read the article here

Novell Storage Manager 3.0 Product Update Released

$
0
0

Novell released an update to Novell Storage Manager 3.0 today that includes a new AuxMap utility and an extensive list of bug fixes.

The AuxMap utility lets you map drive letters to a user’s auxiliary storage folders through command line parameters in a Novell Client login script or a Windows logon script. For more information, see Appendix B in the Novell Storage Manager 3.0 for eDirectory Administration Guide or Appendix C in the Novell Storage Manager 3.0 for Active Directory Administration Guide.

The bug fixes are listed in the new Release Notes document, available from the Novell Storage Manager 3 documentation website at: http://www.novell.com/documentation/storagemanager3/

Novell recommends that all Novell Storage Manager 3.0 customers update their software with this new version. You can download it from the following location: http://download.novell.com/Download?buildid=GbsK6x_T7wE

Using Identity Manager to Detect New AD Domain Controllers

$
0
0

My company provides outsourcing of Identity Management solutions to our clients, where the client continues to operate their own Active Directory environment. One of the challenges of this set of operational boundaries is when our client updates their AD configuration, such as adding a new domain controller, they seldom let us know, nor do they install the mandated password filters.

To help solve this problem, we have crafted a little solution which will notify us by eMail whenever a new domain controller appears on the network.

The solution assumes an already functioning AD driver. This solution provides additional policies that would be added to an existing driver.

Filter

The filter has been enhanced to allow two additional classes with one attribute each. More could have been allowed but it was not necessary for this use case. The schema being allowed through is as follows:

  • Class: domain (representing the domain name in AD)
    • Attribute: Description (contain the domain short name)
  • Class: Computer
    • Attribute: L (containing the DNS name of the domain controller)
<filter-class class-name="Computer" publisher="sync" publisher-create-homedir="true" publisher-track-template-member="true" subscriber="ignore">
		<filter-attr attr-name="L" merge-authority="default" publisher="sync" publisher-optimize-modify="true" subscriber="ignore"/>
	</filter-class>
	<filter-class class-name="domain" publisher="sync" publisher-create-homedir="true" publisher-track-template-member="true" subscriber="ignore">
		<filter-attr attr-name="Description" merge-authority="default" publisher="sync" publisher-optimize-modify="true" subscriber="ignore"/>
	</filter-class>
	

Schema Map

The mapping is as follows:

  • Class domain in eDirectory maps to domainDNS in AD
    • Attribute Description maps to name in AD
  • Class Computer in eDirectory maps to computer in AD
    • Attribute L maps in eDirectory maps to dNSHostName in AD
<class-name>
		<nds-name>domain</nds-name>
		<app-name>domainDNS</app-name>
	</class-name>
	<attr-name class-name="domain">
		<nds-name>Description</nds-name>
		<app-name>name</app-name>
	</attr-name>
	<class-name>
		<nds-name>Computer</nds-name>
		<app-name>computer</app-name>
	</class-name>
	<attr-name class-name="Computer">
		<nds-name>L</nds-name>
		<app-name>dNSHostName</app-name>
	</attr-name>
	

GCVs

I usually place my GCV on the driver set but it could be placed on the driver object itself. However a subsequent article will show some other ways to use the data created by this function which would be dependent on this being assigned to the driver set.

<definition critical-change="true" display-name="Container to place objects representing Domain Controllers" dn-space="dirxml" dn-type="slash" name="cis-dc-landing-container" type="dn">
			<description/>
			<value>lab\idm\Domains</value>
		</definition>
	Additionally, in a multi-domain forest, a GCV is required on each driver which indicates what the root DN for the forest is. This is because despite which domain controller you connect to, the root domain's DN is used to find the configuration container.
		<definition critical-change="true" display-name="Domain in LDAP format which is the root of this domain's forest" dn-space="application" dn-type="ldap" name="AD-Forest-Root" type="dn">
			<description/>
			<value>dc=wwt,dc=corp</value>
		</definition>
		

Publisher Matching Policy

A matching policy is not strictly required for this driver since it's mapping over some fairly static data. However in some configurations the driver will veto in the match rule, so by having this matching policy we can assure that this solution can bolt easily into an existing driver.

<?xml version="1.0" encoding="UTF-8"?><policy>
	<rule>
		<description>[CIS] Domain Matching</description>
		<conditions>
			<and>
				<if-class-name mode="nocase" op="equal">domain</if-class-name>
			</and>
		</conditions>
		<actions>
			<do-find-matching-object scope="entry">
				<arg-dn>
					<token-global-variable name="cis-dc-landing-container"/>
					<token-text xml:space="preserve">\</token-text>
					<token-parse-dn dest-dn-format="slash" src-dn-format="ldap">
						<token-src-dn/>
					</token-parse-dn>
				</arg-dn>
				<arg-match-attr name="Description"/>
			</do-find-matching-object>
			<do-break/>
		</actions>
	</rule>
	<rule>
		<description>[CIS] Domain Contrller Matching</description>
		<conditions>
			<and>
				<if-class-name mode="nocase" op="equal">Computer</if-class-name>
			</and>
		</conditions>
		<actions>
			<do-find-matching-object scope="subtree">
				<arg-dn>
					<token-global-variable name="cis-dc-landing-container"/>
				</arg-dn>
				<arg-match-attr name="l"/>
			</do-find-matching-object>
			<do-break/>
		</actions>
	</rule>
</policy>

Publisher Create Policy

One of the key elements of this is the Publisher Create Policy, This policy will allow only computer objects which are domain controllers. To determine if a computer is a domain controller, the policy will query AD for any objects of class "nTDSDSA". It iterates through those looking at the parent server object comparing the computer object to the "serverReference" attribute value. It will break out of the loop and continue on a match, otherwise, the create is vetoed as it is not a domain controller.

While there might have been more elegant and scalable ways to do this, computers do not join a domain that frequently, and unless your domain is huge, this should not place a huge burden on the infrastructure.

<?xml version="1.0" encoding="UTF-8"?><policy>
	<rule>
		<description>[CIS] Verify Computer is a DC</description>
		<conditions>
			<and>
				<if-class-name mode="nocase" op="equal">Computer</if-class-name>
			</and>
		</conditions>
		<actions>
			<do-for-each>
				<arg-node-set>
					<token-query class-name="nTDSDSA" datastore="src" scope="subtree">
						<arg-dn>
							<token-text xml:space="preserve">CN=Configuration,</token-text>
							<token-global-variable name="AD-Forest-Root"/>
						</arg-dn>
					</token-query>
				</arg-node-set>
				<arg-actions>
					<do-set-local-variable name="DC" scope="policy">
						<arg-string>
							<token-src-attr class-name="server" name="serverReference">
								<arg-dn>
									<token-parse-dn dest-dn-format="ldap" length="-2" src-dn-format="qualified-slash" start="0">
										<token-parse-dn dest-dn-format="qualified-slash" src-dn-format="ldap">
											<token-xpath expression="$current-node/@src-dn"/>
										</token-parse-dn>
									</token-parse-dn>
								</arg-dn>
							</token-src-attr>
						</arg-string>
					</do-set-local-variable>
					<do-if>
						<arg-conditions>
							<and>
								<if-src-dn op="equal">$DC$</if-src-dn>
							</and>
						</arg-conditions>
						<arg-actions>
							<do-break/>
						</arg-actions>
						<arg-actions/>
					</do-if>
				</arg-actions>
			</do-for-each>
			<do-veto/>
		</actions>
	</rule>
</policy>

Publisher Placement Policy

The placement policy handles two different classes, the domain objects (of which each AD domain has one and only one) and the computer objects.

A domain is represented in eDirectory as individual objects for each level of the hierarchy. In AD, the LDAP name looks like it would be represented that way, but the domainDNS object is really akin to the Tree object in eDirectory, the root of the name space.

When syncing a domain, the driver will split up the domain into levels, then iterate from the top to 1 level above the bottom to create (if necessary) placeholder objects to store the domain in. Then the actual domain object (which gets a DirXML-Association) is represented by it's bottom level.

Domain controllers are represented as Computer objects, named for the server name, and placed directly under the domain (again it was structured like this for a future project)

<?xml version="1.0" encoding="UTF-8"?><policy>
	<rule>
		<description>[CIS] Create Domain Structure</description>
		<comment xml:space="preserve">Breaks down the dpmain into separate objects for each level</comment>
		<conditions>
			<and>
				<if-class-name mode="nocase" op="equal">domain</if-class-name>
			</and>
		</conditions>
		<actions>
			<do-set-local-variable name="LEVELS" scope="policy">
				<arg-node-set>
					<token-split delimiter=",">
						<token-src-dn/>
					</token-split>
				</arg-node-set>
			</do-set-local-variable>
			<do-set-local-variable name="DEX" scope="policy">
				<arg-string>
					<token-xpath expression="string(count($LEVELS))"/>
				</arg-string>
			</do-set-local-variable>
			<do-while>
				<arg-conditions>
					<and>
						<if-local-variable mode="nocase" name="DEX" op="not-equal">1</if-local-variable>
					</and>
				</arg-conditions>
				<arg-actions>
					<do-set-local-variable name="LDAP-NAME" scope="policy">
						<arg-string>
							<token-xpath expression="$LEVELS[number($DEX)]"/>
							<token-text xml:space="preserve">,</token-text>
							<token-local-variable name="LDAP-NAME"/>
						</arg-string>
					</do-set-local-variable>
					<do-set-local-variable name="NDAP-NAME" scope="policy">
						<arg-string>
							<token-global-variable name="cis-dc-landing-container"/>
							<token-text xml:space="preserve">\</token-text>
							<token-parse-dn dest-dn-format="slash" src-dn-format="ldap">
								<token-xpath expression="substring($LDAP-NAME,1,string-length($LDAP-NAME)-1)"/>
							</token-parse-dn>
						</arg-string>
					</do-set-local-variable>
					<do-set-local-variable name="OBJ" scope="policy">
						<arg-node-set>
							<token-query class-name="domain" scope="entry">
								<arg-dn>
									<token-local-variable name="NDAP-NAME"/>
								</arg-dn>
							</token-query>
						</arg-node-set>
					</do-set-local-variable>
					<do-if>
						<arg-conditions>
							<and>
								<if-xpath op="true">count($OBJ)=0</if-xpath>
							</and>
						</arg-conditions>
						<arg-actions>
							<do-add-dest-object class-name="domain" direct="true">
								<arg-dn>
									<token-local-variable name="NDAP-NAME"/>
								</arg-dn>
							</do-add-dest-object>
						</arg-actions>
						<arg-actions/>
					</do-if>
					<do-set-local-variable name="DEX" scope="policy">
						<arg-string>
							<token-xpath expression="string(number($DEX)-1)"/>
						</arg-string>
					</do-set-local-variable>
				</arg-actions>
			</do-while>
		</actions>
	</rule>
	<rule>
		<description>[CIS] DC Placement</description>
		<conditions>
			<and>
				<if-class-name mode="regex" op="equal">Computer|domain</if-class-name>
			</and>
		</conditions>
		<actions>
			<do-set-local-variable name="DOMAIN-PART" scope="policy">
				<arg-string>
					<token-lower-case>
						<token-src-dn/>
					</token-lower-case>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="DOMAIN-PART" scope="policy">
				<arg-string>
					<token-text xml:space="preserve">dc=</token-text>
					<token-xpath expression='substring-after($DOMAIN-PART,"dc=")'/>
				</arg-string>
			</do-set-local-variable>
			<do-if>
				<arg-conditions>
					<and>
						<if-class-name mode="nocase" op="equal">Computer</if-class-name>
					</and>
				</arg-conditions>
				<arg-actions>
					<do-set-op-dest-dn>
						<arg-dn>
							<token-global-variable name="cis-dc-landing-container"/>
							<token-text xml:space="preserve">\</token-text>
							<token-parse-dn dest-dn-format="slash" src-dn-format="ldap">
								<token-local-variable name="DOMAIN-PART"/>
							</token-parse-dn>
							<token-text xml:space="preserve">\</token-text>
							<token-src-name/>
						</arg-dn>
					</do-set-op-dest-dn>
				</arg-actions>
				<arg-actions>
					<do-set-op-dest-dn>
						<arg-dn>
							<token-global-variable name="cis-dc-landing-container"/>
							<token-text xml:space="preserve">\</token-text>
							<token-parse-dn dest-dn-format="slash" src-dn-format="ldap">
								<token-local-variable name="DOMAIN-PART"/>
							</token-parse-dn>
						</arg-dn>
					</do-set-op-dest-dn>
				</arg-actions>
			</do-if>
			<do-break/>
		</actions>
	</rule>
</policy>

Input Transform

The serverReference attribute is a stored in a DN reference in AD, which would map to a DN reference in eDir except we don't have one. So this policy changes the reference into a string so we can synchronize it irrespective of whether or not we have a related object in eDirectory.

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE policy PUBLIC "policy-builder-dtd""/home/rrawson/designer/plugins/com.novell.idm.policybuilder_3.5.0.201003011501/DTD/dirxmlscript3.6.1.dtd"><policy>
	<rule>
		<description>[CIS] Make serverReference a string (test)</description>
		<comment xml:space="preserve">This allows queries of the serverReference attribute through on a server object, it server objects are not synced, is entirely to verify that a computer object is in fact a doman controller</comment>
		<comment name="author" xml:space="preserve">RR</comment>
		<conditions>
			<and>
				<if-operation mode="case" op="equal">instance</if-operation>
				<if-op-attr name="serverReference" op="available"/>
			</and>
		</conditions>
		<actions>
			<do-strip-xpath expression='attr[@attr-name="serverReference"]/value/@type'/>
			<do-strip-xpath expression='attr[@attr-name="serverReference"]/value/@naming'/>
			<do-strip-xpath expression='attr[@attr-name="serverReference"]/value/@association-ref'/>
		</actions>
	</rule>
</policy>

Command Transform

The command transform has a simple policy – if a computer object is added, we will send an eMail to an administrator reminding them to install the password filter.

<?xml version="1.0" encoding="UTF-8"?><policy>
	<rule>
		<description>[CIS] Domain Controller Add</description>
		<conditions>
			<and>
				<if-class-name mode="nocase" op="equal">Computer</if-class-name>
				<if-operation mode="case" op="equal">add</if-operation>
			</and>
		</conditions>
		<actions>
			<do-send-email-from-template notification-dn="Security\Default Notification Collection" template-dn="Security\Default Notification Collection\[CIS] Domain Controller Notification">
				<arg-string name="to">
					<token-text xml:space="preserve">wattssupport@ciscony.com</token-text>
				</arg-string>
				<arg-string name="DCAddress">
					<token-attr name="L"/>
				</arg-string>
				<arg-string name="DomainName">
					<token-dest-dn start="2"/>
					<token-dest-attr name="Description">
						<arg-dn>
							<token-dest-dn start="2"/>
						</arg-dn>
					</token-dest-attr>
				</arg-string>
			</do-send-email-from-template>
		</actions>
	</rule>
</policy>

In order to support this, a simple eMail template is necessary:

<template name="[CIS] Domain Controller Notification" subject="New Domain Controller Detected in Domain $DomainName$">
	<data>
		<form:text xmlns:form="http://www.novell.com/dirxml/workflow/form">
			<form:token-descriptions>
				<form:token-description description="" item-name="DomainName"/>
				<form:token-description description="" item-name="DCAddress"/>
			</form:token-descriptions>To Whom It May Concern:

A new domain controller has been added to Active Directory domain $DomainName$ at address $DCAddress$.

Please check to make certain that the password filter has been installed and the domain controller has been rebooted.

Thank you
CIS 
</form:text>
	</data>
</template>

Implementation

The recommended sequence for testing this solution once in place is as follows:

  1. Migrate the domain objects. You should see the domain structure built out in the identified container.
  2. Migrate a single domain controller as a computer object by name. This should appear under the domain as a computer.
  3. During a "quiet" time, migrate all computers. There will be a fair amount of activity during the migration which could cause other synchronization to queue up behind this, so this should be done during off hours if possible.

Conclusion

This solution provides a way to get operational data from Active Directory into your identity vault. In addition to simple notification, other envisioned applications include:

  • Synchronizing accurate AD Login Times from all domain controllers
  • Automating password filter installation

Future articles may discuss these solutions and more.

AttachmentSize
using_identity_manager_to_detect_new_ad_domain_controllers.doc63.5 KB

Using the Scripting Driver to query Active Directory

$
0
0

In a prior article, I discussed an enhancement to the Active Directory Driver configuration which would allow you to create objects in the Identity Vault which represent the Active Directory domain. In this article, I will discuss one use for those objects.

Our client is concerned about a well known behavior in AD with regards to synchronization of the last logon time of a user. In Active Directory, the attribute lastLogon contains the last time a user logged in. This attribute, however, is not replicated between domain controllers. Therefore, since IDM runs off one of those domain controllers, only user who logged in at that specific DC would be synchronized.

In Windows 2003 they introduced another attribute, lastLogonTimestamp. This attribute does synchronize between domain controllers, but by default this happens no more often than once every 9 to 14 days. Assuming the typical user is logging in daily, this means that the value of this attribute could be up to two weeks stale.

In addition for certain logons, the attribute never gets updated unless AD is properly patched (see http://support.microsoft.com/kb/886705).

Now for most every application this is sufficient; you usually want to know that a user has been logging in over the last month or three to determine whether they are still a living breathing human using the workstation. However, if you are of the species auditor, you might have a different perspective, that the numbers need to be as perfect as possible.

In this case a monthly auditing report was produced and the dates were stale as we noted above, and therefore the client wanted a process to gather more accurate data.

We first utilized a simple VBA macro to accomplish this, but since they have 5 domains, this meant running a report against 15 different domain controllers using 5 different sets of credentials along with a macro which used the Novell ActiveX controls to report out the identity vault data, then reconcile this with another macro. It was long, it was unpleasant, and because it required a user with administrative privileges to run, and that was the same user that ran the domain, it violated auditing separation of duties principles.

So we whipped up a quick solution using the scripting driver (which thankfully they had already licensed) to accomplish this task. The solution does two things:

It maintains a multi-valued attribute of SYN_TYPED_NAME which is a structured attribute containing a DN and two bit unsigned integers (“level” and “interval”). Since time is internally stored as unsigned 32 bit integers, this was an appropriate use of the schema.

This attribute is used to store the DN of the domain controller representation in the identity Vault, the user’s last logon time at that DC, and the time the data was read respectively.

Second, the driver will compare the Last Login time from the Identity vault to the new value read from AD, which ever one is later will be stored as the Login Time in the Identity Vault.

The Solution

This client has a dozen or more AD domains, but in scope for this was six Active Directory domains in two forests. Five of the domains are synchronized into our identity vault. To deal with the two different name spaces, we store the data in separate attributes. Therefore, we maintain a GCV which contains the name of the attribute which contains the AD DN.

Note: For the additional domains, we defined a methodology of “plural” Active Directory attributes, i.e, DirXML-ADContexts and DirXML-ADAliasNames which are structured attributes containing the driver DN and the value for that tree. If it hasn’t already been documented by Geoff Carman, I will document that in a future article.

There are three parts to this solution:

  • Driver Policies
  • Script
  • Trigger Job

The basic concept is that a subscriber channel trigger fires once per night for all users; this trigger sends one trigger document per user. For each user, we will perform a query of Active Directory via the scripting driver using a specially formed class name which will direct the queries to the correct domain controller. The domain controllers are determined by the policies documented in a prior article on how to synchronize a representation of them from AD to the identity vault,

Driver Policies

GCVs

The following GCV is defined to indirect the name of the attribute containing the user’s AD DN in this name space:

<header display-name="CIS Custom Attributes"/>
		<definition display-name="Attribute containing context in this tree of this user" name="cis-ADContext-Attribute" type="string">
			<value>wattsLegacyADContext</value>
		</definition>

Subscriber Event Transform

This policy recognizes the trigger event and takes the following actions:

  1. It gets the DN of the user from whichever attribute is storing the AD DN for this tree. Since there a multiple domains and users may be a member of multiple domains, this allows us to find the correct DN for the instance of the driver we are running.
  2. It parses the domain part off the user’s Active Directory DN. We will use this later to calculate the context to find representations of the domain controllers.
  3. We set a node-set variable containing the current list of last logon times from the identity vault
  4. We initialize a variable to indicate the latest logon time across the various domain controllers.
  5. We iterate through list of computer objects at the domain location we created in the identity vault. For each object we take the following actions:
    1. Get the DN and the DNS name of the domain controller
    2. Call the script to get the last logon time from that DC
    3. If we get a value
      1. Iterate through and remove old last logon times from the identity vault list
      2. Update the list with the new value
      3. Calculate the latest login time from all the domains
      4. If the latest login time is later than the identity vault login time, update the identity vault to match.
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE policy PUBLIC "policy-builder-dtd""/home/rrawson/designer/plugins/com.novell.idm.policybuilder_3.5.0.201003011501/DTD/dirxmlscript3.6.dtd"><policy>
	<rule>
		<description>[CIS] Trigger</description>
		<conditions>
			<and>
				<if-operation mode="case" op="equal">trigger</if-operation>
				<if-attr name="DirXML-ADContext" op="available"/>
			</and>
		</conditions>
		<actions>
			<do-set-local-variable name="USER-DN" scope="policy">
				<arg-string>
					<token-lower-case>
						<token-src-attr name="~cis-ADContext-Attribute~"/>
					</token-lower-case>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="DOMAIN-PART" scope="policy">
				<arg-string>
					<token-text xml:space="preserve">dc=</token-text>
					<token-xpath expression='substring-after($USER-DN,"dc=")'/>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="AD-LAST-LOGONS" scope="policy">
				<arg-node-set>
					<token-src-attr name="wattsADlastLogons"/>
				</arg-node-set>
			</do-set-local-variable>
			<do-set-local-variable name="LATEST-LOGON" scope="policy">
				<arg-string>
					<token-text xml:space="preserve">0</token-text>
				</arg-string>
			</do-set-local-variable>
			<do-for-each>
				<arg-node-set>
					<token-query class-name="Computer" datastore="src" scope="subordinates">
						<arg-dn>
							<token-global-variable name="cis-dc-landing-container"/>
							<token-text xml:space="preserve">\</token-text>
							<token-parse-dn dest-dn-format="slash" src-dn-format="ldap">
								<token-local-variable name="DOMAIN-PART"/>
							</token-parse-dn>
						</arg-dn>
						<arg-string>
							<token-text xml:space="preserve">L</token-text>
						</arg-string>
					</token-query>
				</arg-node-set>
				<arg-actions>
					<do-set-local-variable name="DC-SRC-DN" scope="policy">
						<arg-string>
							<token-xpath expression="$current-node/@src-dn"/>
						</arg-string>
					</do-set-local-variable>
					<do-set-local-variable name="DC-ADDRESS" scope="policy">
						<arg-string>
							<token-xpath expression="$current-node/attr[@attr-name='L']/value/text()"/>
						</arg-string>
					</do-set-local-variable>
					<do-set-local-variable name="LAST-LOGON" scope="policy">
						<arg-string>
							<token-query class-name="nTDSDSA@$DC-ADDRESS$" scope="entry">
								<arg-dn>
									<token-local-variable name="USER-DN"/>
								</arg-dn>
								<arg-string>
									<token-text xml:space="preserve">lastLogon</token-text>
								</arg-string>
							</token-query>
						</arg-string>
					</do-set-local-variable>
					<do-for-each>
						<arg-node-set>
							<token-local-variable name="AD-LAST-LOGONS"/>
						</arg-node-set>
						<arg-actions>
							<do-if>
								<arg-conditions>
									<and>
										<if-xpath op="true">$current-node/component[@name='dn']/text()=$DC-SRC-DN</if-xpath>
									</and>
								</arg-conditions>
								<arg-actions>
									<do-remove-src-attr-value name="wattsADlastLogons">
										<arg-value type="structured">
											<arg-component name="dn">
												<token-xpath expression="$current-node/component[@name='dn']/text()"/>
											</arg-component>
											<arg-component name="level">
												<token-xpath expression="$current-node/component[@name='level']/text()"/>
											</arg-component>
											<arg-component name="interval">
												<token-xpath expression="$current-node/component[@name='interval']/text()"/>
											</arg-component>
										</arg-value>
									</do-remove-src-attr-value>
								</arg-actions>
								<arg-actions/>
							</do-if>
						</arg-actions>
					</do-for-each>
					<do-if>
						<arg-conditions>
							<and>
								<if-local-variable mode="nocase" name="LAST-LOGON" op="not-equal"/>
							</and>
						</arg-conditions>
						<arg-actions>
							<do-add-src-attr-value name="wattsADlastLogons">
								<arg-value type="structured">
									<arg-component name="dn">
										<token-xpath expression="$current-node/@src-dn"/>
									</arg-component>
									<arg-component name="level">
										<token-local-variable name="LAST-LOGON"/>
									</arg-component>
									<arg-component name="interval">
										<token-time format="!CTIME" tz="UTC"/>
									</arg-component>
								</arg-value>
							</do-add-src-attr-value>
							<do-if>
								<arg-conditions>
									<and>
										<if-attr name="Login Time" op="available"/>
									</and>
								</arg-conditions>
								<arg-actions>
									<do-set-local-variable name="LOGIN-TIME" scope="policy">
										<arg-string>
											<token-attr name="Login Time"/>
										</arg-string>
									</do-set-local-variable>
								</arg-actions>
								<arg-actions>
									<do-set-local-variable name="LOGIN-TIME" scope="policy">
										<arg-string>
											<token-text xml:space="preserve">1</token-text>
										</arg-string>
									</do-set-local-variable>
								</arg-actions>
							</do-if>
							<do-if>
								<arg-conditions>
									<and>
										<if-xpath op="true">number($LATEST-LOGIN) < number($LAST-LOGIN)</if-xpath>
									</and>
								</arg-conditions>
								<arg-actions>
									<do-if>
										<arg-conditions>
											<and>
												<if-xpath op="true">number($LAST-LOGON) > number($LOGIN-TIME)</if-xpath>
											</and>
										</arg-conditions>
										<arg-actions>
											<do-set-src-attr-value name="Login Time">
												<arg-value type="time">
													<token-local-variable name="LAST-LOGON"/>
												</arg-value>
											</do-set-src-attr-value>
										</arg-actions>
										<arg-actions/>
									</do-if>
									<do-set-local-variable name="LATEST-LOGON" scope="policy">
										<arg-string>
											<token-local-variable name="LAST-LOGON"/>
										</arg-string>
									</do-set-local-variable>
								</arg-actions>
								<arg-actions/>
							</do-if>
						</arg-actions>
						<arg-actions/>
					</do-if>
				</arg-actions>
			</do-for-each>
		</actions>
	</rule>
	<rule>
		<description>[CIS] Screen Triggers from Shim</description>
		<conditions>
			<and>
				<if-operation mode="case" op="equal">trigger</if-operation>
			</and>
		</conditions>
		<actions>
			<do-veto/>
		</actions>
	</rule>
</policy>

Input Transform

Originally, I planned to utilize the existing policies which convert AD time to CTIME, however I decided that it would take more time to alter the existing script I had to remove it’s time formatting than to implement the convert time token.

However, the date and time comes through in a format that does not have leading zeroes on the single digit numbers, which meant I still had to parse the time string discretely. Perhaps more time spent on the convert time token would have resulted in a simpler solution but I for this project implementation time was key.

This policy simply takes the string which contains the date and adds the necessary leading zeros, then puts it back together to pass through the convert time token.

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE policy PUBLIC "policy-builder-dtd""/home/rrawson/designer/plugins/com.novell.idm.policybuilder_3.5.0.201003011501/DTD/dirxmlscript3.6.dtd"><policy>
	<rule>
		<description>[CIS] lastLogon from script to CTIME</description>
		<conditions>
			<and>
				<if-op-attr name="lastLogon" op="available"/>
			</and>
		</conditions>
		<actions>
			<do-set-local-variable name="DATESPLIT" scope="policy">
				<arg-node-set>
					<token-split delimiter="/">
						<token-op-attr name="lastLogon"/>
					</token-split>
				</arg-node-set>
			</do-set-local-variable>
			<do-set-local-variable name="MONTH" scope="policy">
				<arg-string>
					<token-text xml:space="preserve">0</token-text>
					<token-xpath expression="$DATESPLIT[1]"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="DAY" scope="policy">
				<arg-string>
					<token-text xml:space="preserve">0</token-text>
					<token-xpath expression="$DATESPLIT[2]"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="YEAR" scope="policy">
				<arg-string>
					<token-substring length="4">
						<token-xpath expression="$DATESPLIT[3]"/>
					</token-substring>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="TIMESPLIT" scope="policy">
				<arg-node-set>
					<token-split delimiter=":">
						<token-substring start="5">
							<token-xpath expression="$DATESPLIT[3]"/>
						</token-substring>
					</token-split>
				</arg-node-set>
			</do-set-local-variable>
			<do-set-local-variable name="HOUR" scope="policy">
				<arg-string>
					<token-text xml:space="preserve">0</token-text>
					<token-xpath expression="$TIMESPLIT[1]"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="MIN" scope="policy">
				<arg-string>
					<token-text xml:space="preserve">0</token-text>
					<token-xpath expression="$TIMESPLIT[1]"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="MERIDIANSPLIT" scope="policy">
				<arg-node-set>
					<token-split delimiter="">
						<token-xpath expression="$TIMESPLIT[3]"/>
					</token-split>
				</arg-node-set>
			</do-set-local-variable>
			<do-set-local-variable name="SECS" scope="policy">
				<arg-string>
					<token-text xml:space="preserve">0</token-text>
					<token-xpath expression="$MERIDIANSPLIT[1]"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="MERIDIAN" scope="policy">
				<arg-string>
					<token-xpath expression="$MERIDIANSPLIT[2]"/>
				</arg-string>
			</do-set-local-variable>
			<do-set-local-variable name="FMT-TIME" scope="policy">
				<arg-string>
					<token-xpath expression="substring($MONTH,string-length($MONTH)-2+1)"/>
					<token-text xml:space="preserve">/</token-text>
					<token-xpath expression="substring($DAY,string-length($DAY)-2+1)"/>
					<token-text xml:space="preserve">/</token-text>
					<token-local-variable name="YEAR"/>
					<token-text xml:space="preserve"> </token-text>
					<token-xpath expression="substring($HOUR,string-length($HOUR)-2+1)"/>
					<token-text xml:space="preserve">:</token-text>
					<token-xpath expression="substring($MIN,string-length($MIN)-2+1)"/>
					<token-text xml:space="preserve">:</token-text>
					<token-xpath expression="substring($SECS,string-length($SECS)-2+1)"/>
					<token-text xml:space="preserve"> </token-text>
					<token-local-variable name="MERIDIAN"/>
				</arg-string>
			</do-set-local-variable>
			<do-reformat-op-attr name="lastLogon">
				<arg-value type="time">
					<token-convert-time dest-format="!CTIME" dest-tz="UTC" src-format="MM/dd/yyyy hh:mm:ss a">
						<token-local-variable name="FMT-TIME"/>
					</token-convert-time>
				</arg-value>
			</do-reformat-op-attr>
		</actions>
	</rule>
</policy>

Script

The scripting driver allows you to run any script at the time of an event from eDirectory. The script is copied to the QUERY.VBS script. This script was adapted from a commonly used VBS script found through a Google search.

The scripting driver runs as a Windows service, and by default the service installs as the local system account. This will not suffice when querying Active Directory, and therefore the service must be stopped and reconfigured to log on as a user which has domain administrative privileges.

This script expects that the query sent by the IDM driver will contain a class name which is set to “nTDSDSA@<dns-name-of-DC>”. This is our flag to execute the query for the selected attribute. The attribute name is passed through from the query document, however this particular script is hard coded to expect that this is a date attribute, and therefore would not work for other attribute syntaxes without modification. It is not clear what other attributes in AD that do not synchronize would be of interest, however.

Sub QUERY
  ' *****************************************
  ' * Add implementation-specific code here *
  ' * Set the command to INSTANCE           *
   ' *****************
' Derived from LastLogon.vbs
' VBScript program to determine when each user in the domain last logged
' on.
'' ----------------------------------------------------------------------
' Copyright (c) 2002 Richard L. Mueller
' Hilltop Lab web site - http://www.rlmueller.net' Novell IDM Scripting Driver Query adaptation 
' Copyright © 2010 Robert Rawson DBA Rare Progeny Consulting
'  for Computer Integrated Services of NY LLC http://www.ciscony.com' Version 1.0 - December 7, 2002
' Version 1.1 - January 17, 2003 - Account for null value for lastLogon.
' Version 1.2 - January 23, 2003 - Account for DC not available.
' Version 1.3 - February 3, 2003 - Retrieve users but not contacts.
' Version 1.4 - February 19, 2003 - Standardize Hungarian notation.
' Version 1.5 - March 11, 2003 - Remove SearchScope property.
' Version 1.6 - May 9, 2003 - Account for error in IADsLargeInteger
'                             property methods HighPart and LowPart.
' Version 1.7 - January 25, 2004 - Modify error trapping.
' Version 1.8 - July 6, 2007 - Modify how IADsLargeInteger interface
'                              is invoked.
' Version 1.9 - December 29, 2009 - Output "Never" if no date.
' UPDATE: 1.10 – September 28, 2010 by Robert Rawson of CIS
'                Added filtering for use in multi-domain forests
'' Because the lastLogon attribute is not replicated, every Domain
' Controller in the domain must be queried to find the latest lastLogon
' date for each user. The lastest date found is kept in a dictionary
' object. The program first uses ADO to search the domain for all Domain
' Controllers. The AdsPath of each Domain Controller is saved in an
' array. Then, for each Domain Controller, ADO is used to search the
' copy of Active Directory on that Domain Controller for all user
' objects and return the lastLogon attribute. The lastLogon attribute is
' a 64-bit number representing the number of 100 nanosecond intervals
' since 12:00 am January 1, 1601. This value is converted to a date. The
' last logon date is in UTC (Coordinated Univeral Time). It must be
' adjusted by the Time Zone bias in the machine registry to convert to
' local time.
'' You have a royalty-free right to use, modify, reproduce, and
' distribute this script file in any way you find useful, provided that
' you agree that the copyright owner above has no warranty, obligations,
' or liability for such use.
dim strSearchName
dim strSearchNames
dim strClassName
dim strDCDNSName
dim strBase
dim strAttributes
dim strQuery
dim strFilter
dim strDN
dim adoCommand
dim adoConnection
dim adoRecordset
dim objDate
dim dtmDate
dim lngHigh
dim lngLow
dim objShell
dim lngBiasKey
dim lngBias

IDMTrace("Query Started")
IDMSetCommand "INSTANCE"' Obtain local Time Zone bias from machine registry.
' This bias changes with Daylight Savings Time.
Set objShell = CreateObject("Wscript.Shell")
lngBiasKey = objShell.RegRead("HKLM\System\CurrentControlSet\Control\" _
    & "TimeZoneInformation\ActiveTimeBias")
If (UCase(TypeName(lngBiasKey)) = "LONG") Then
    lngBias = lngBiasKey
ElseIf (UCase(TypeName(lngBiasKey)) = "VARIANT()") Then
    lngBias = 0
    For k = 0 To UBound(lngBiasKey)
        lngBias = lngBias + (lngBiasKey(k) * 256^k)
    Next
End If

' this is a constant which is what the IDM driver will use to indicate it is querying a specific DC
Const strFlagClass = "nTDSDSA@"

strClassName = IDMGetEventValue("CLASS_NAME")
IDMTrace("Class Name String: "& strClassName)
IDMWriteValue "CLASS_NAME", strClassName

If left(strClassName,8) <> strFlagClass then
    IDMStatusError "Query: class name must be "& strFlagName & "<dns-name-of-dc>"
    exit sub
    end if

' get the attribute name to search. Note this is hard coded to date attributes for now
strSearchName = IDMGetEventValue("READ_ATTRS")


If IsEmpty(strSearchName) Then
    IDMStatusError "Query: no search value"
    exit sub
    end if


     IDMTrace("Query attribute: "& strSearchName)

     Set adoCommand = CreateObject("ADODB.Command")
     Set adoConnection = CreateObject("ADODB.Connection")
     adoConnection.Provider = "ADsDSOObject"
     adoConnection.Open "Active Directory Provider"
     adoCommand.ActiveConnection = adoConnection

     strDCDNSName = right(strClassName, Len(strClassName)-Len(strFlagClass))

     strBase = "<LDAP://"& strDCDNSName  & "/"& IDMGetEventValue("DEST_DN") & ">"
     strFilter = "(&(objectCategory=person)(objectClass=user))"
     strAttributes = "distinguishedName," + strSearchName
     strQuery = strBase & ";"& strFilter & ";"& strAttributes & ";subtree"
     adoCommand.CommandText = strQuery

     IDMTrace("Query string: "& strQuery)

     On Error Resume Next
     Set adoRecordset = adoCommand.Execute

     If (Err.Number <> 0) Then
         IDMStatusError "Domain Controller "& strDCDNSName & " not available ("& cstr(err.number) & ") :"& err.description
         On Error GoTo 0
         exit sub
         end if

     On Error GoTo 0

     Do Until adoRecordset.EOF

         strDN = adoRecordset.Fields("distinguishedName").Value
         IDMTrace("Object Found: "& strDN)

         On Error Resume Next
         Set objDate = adoRecordset.Fields(strSearchName).Value

         If (Err.Number <> 0) Then
             IDMStatusError "Error retrieving "& strSearchName & " from "& strDCDNSName & "("& cstr(err.number) & ") :"& err.description
             On Error GoTo 0
             exit sub
             End If

         On Error GoTo 0

         lngHigh = objDate.HighPart
         lngLow = objDate.LowPart

         If (lngLow < 0) Then
             lngHigh = lngHigh + 1
             End If

         If (lngHigh = 0) And (lngLow = 0) Then
             dtmDate = #1/1/1601#
         Else
             dtmDate = #1/1/1601# + (((lngHigh * (2 ^ 32)) _
                + lngLow)/600000000 - lngBias)/1440
             End If

         IDMTrace("Returning "& strSearchName & "="& cstr(dtmDate))
         IDMWriteValue "ATTR_lastLogon", dtmDate

         adoRecordset.MoveNext
         Loop
    adoRecordset.Close

IDMStatusSuccess "Query from DC at "& strDCDNSName & " successful."

End Sub

The Job

The final element of this solution is a trigger job. The job is defined in iManager as follows:

Run the job every night at 1:00 am

Apply to users only

Send the events to all objects in scope

The XML of the job definition is as follows:

<?xml version="1.0" encoding="UTF-8"?><job-aggregation>
   <job-definition auto-delete="false" disabled="false" display-name="xlfid(job-display-name)Subscriber channel trigger" schedule="59 0 * * *" scope-required="false" type="java">
      <description>xlfid(job-description)This job submits zero or more trigger documents to the subscriber channel. The submission may either be a document per object if a scope is defined or may be a single document for each job run.</description>
      <containment>DirXML-Driver</containment>
      <java-class>com.novell.nds.dirxml.job.trigger.Trigger</java-class>
      <configuration-values>
         <definitions>
            <definition display-name="xlfid(process-unassociated)Submit a trigger document for objects without a driver association?" name="process-unassociated" type="boolean">
               <value>true</value>
            </definition>
            <group>
               <definition display-name="xlfid(use-job-cn)Use Job CN as trigger document identifier?" name="use-job-cn" type="boolean">
                  <description>xlfid(use-job-cn-desc)If set, use the job object's CN as the value of the trigger element's "source" attribute.</description>
                  <value>true</value>
               </definition>
               <subordinates active-value="false">
                  <definition display-name="xlfid(trigger-source)Trigger element source value" name="trigger-source" type="string">
                     <description>xlfid(trigger-source-desc)String to use as the value for the trigger element's "source" attribute.</description>
                     <value>1</value>
                  </definition>
               </subordinates>
            </group>
            <group>
               <definition display-name="xlfid(submit-method)Method for submitting trigger documents" name="submit-method" type="enum">
                  <enum-choice display-name="xlfid(submit-queue)queue (use cache)">submit-queue</enum-choice>
                  <enum-choice display-name="xlfid(submit-direct)direct (bypass cache)">submit-direct</enum-choice>
                  <value>submit-queue</value>
               </definition>
               <subordinates active-value="submit-direct">
                  <group>
                     <definition display-name="xlfid(start-driver)Start driver if not running" name="start-driver" type="boolean">
                        <value>false</value>
                     </definition>
                     <subordinates active-value="true">
                        <definition display-name="xlfid(stop-driver)Stop driver when finished processing trigger(s)" name="stop-driver" type="boolean">
                           <value>true</value>
                        </definition>
                     </subordinates>
                  </group>
               </subordinates>
            </group>
         </definitions>
      </configuration-values>
   </job-definition>
   <xliff version="1.0">
      <file datatype="xliff" original="Trigger.xml" source-language="de" xml:space="preserve">
      <header/>
      <body>
         <trans-unit id="job-display-name">
            <source>Abonnentenkanalauslöser</source>
         </trans-unit>
         <trans-unit id="job-description">
            <source>Mit diesem Auftrag werden null oder mehr Auslöserdokumente an den Abonnentenkanal abgesendet. Dabei kann es sich um ein Dokument pro Objekt handeln, sofern ein Bereich definiert wurde, oder um ein einzelnes Dokument für jede Auftragsausführung.</source>
         </trans-unit>
         <trans-unit id="process-unassociated">
            <source>Auslöserdokument für Objekte ohne Treiberzuordnung absenden?</source>
         </trans-unit>
         <trans-unit id="use-job-cn">
            <source>Auftrags-CN als Auslöserdokumentbezeichner verwenden?</source>
         </trans-unit>
         <trans-unit id="use-job-cn-desc">
            <source>Wenn festgelegt, wird der CN des Auftragsobjekts als Wert für das Ursprungsattribut des Auslöserelements verwendet.</source>
         </trans-unit>
         <trans-unit id="trigger-source">
            <source>Ursprungswert des Auslöserelements</source>
         </trans-unit>
         <trans-unit id="trigger-source-desc">
            <source>Zeichenkette, die als Wert für das Ursprungsattribut des Auslöserelements verwendet werden soll.</source>
         </trans-unit>
         <trans-unit id="submit-method">
            <source>Methode für das Absenden von Auslöserdokumenten</source>
         </trans-unit>
         <trans-unit id="submit-queue">
            <source>Warteschlange (Cache verwenden)</source>
         </trans-unit>
         <trans-unit id="submit-direct">
            <source>Direkt (Cache übergehen)</source>
         </trans-unit>
         <trans-unit id="start-driver">
            <source>Treiber starten, sofern er nicht bereits läuft</source>
         </trans-unit>
         <trans-unit id="stop-driver">
            <source>Treiber nach Verarbeiten von Auslösern anhalten</source>
         </trans-unit>
      </body>
   </file>
   </xliff>
   <xliff version="1.0">
      <file datatype="xliff" original="Trigger.xml" source-language="en" xml:space="preserve">
      <header/>
      <body>
         <trans-unit id="job-display-name">
            <source>Subscriber channel trigger</source>
         </trans-unit>
         <trans-unit id="job-description">
            <source>This job submits zero or more trigger documents to the subscriber channel. The submission may either be a document per object if a scope is defined or may be a single document for each job run.</source>
         </trans-unit>
         <trans-unit id="process-unassociated">
            <source>Submit a trigger document for objects without a driver association?</source>
         </trans-unit>
         <trans-unit id="use-job-cn">
            <source>Use Job CN as trigger document identifier?</source>
         </trans-unit>
         <trans-unit id="use-job-cn-desc">
            <source>If set, use the job object's CN as the value of the trigger element's "source" attribute.</source>
         </trans-unit>
         <trans-unit id="trigger-source">
            <source>Trigger element source value</source>
         </trans-unit>
         <trans-unit id="trigger-source-desc">
            <source>String to use as the value for the trigger element's "source" attribute.</source>
         </trans-unit>
         <trans-unit id="submit-method">
            <source>Method for submitting trigger documents</source>
         </trans-unit>
         <trans-unit id="submit-queue">
            <source>queue (use cache)</source>
         </trans-unit>
         <trans-unit id="submit-direct">
            <source>direct (bypass cache)</source>
         </trans-unit>
         <trans-unit id="start-driver">
            <source>Start driver if not running</source>
         </trans-unit>
         <trans-unit id="stop-driver">
            <source>Stop driver when finished processing trigger(s)</source>
         </trans-unit>
      </body>
   </file>
   </xliff>
   <xliff version="1.0">
      <file datatype="xliff" original="Trigger.xml" source-language="fr" xml:space="preserve">
      <header/>
      <body>
         <trans-unit id="job-display-name">
            <source>Déclencheur de canal abonné</source>
         </trans-unit>
         <trans-unit id="job-description">
            <source>Ce travail adresse un ou plusieurs documents déclencheurs (ou n'en soumet pas) au canal abonné. Il peut envoyer un document par objet, si une étendue a été définie, ou un seul document à chaque exécution.</source>
         </trans-unit>
         <trans-unit id="process-unassociated">
            <source>Soumettre un document déclencheur pour les objets sans association de pilote ?</source>
         </trans-unit>
         <trans-unit id="use-job-cn">
            <source>Utiliser le CN du travail comme identificateur de document déclencheur ?</source>
         </trans-unit>
         <trans-unit id="use-job-cn-desc">
            <source>Le cas échéant, utiliser le CN de l'objet Travail comme valeur de l'attribut "source" de l'élément déclencheur.</source>
         </trans-unit>
         <trans-unit id="trigger-source">
            <source>Valeur source de l'élément déclencheur</source>
         </trans-unit>
         <trans-unit id="trigger-source-desc">
            <source>Chaîne à utiliser comme valeur de l'attribut "source" de l'élément déclencheur</source>
         </trans-unit>
         <trans-unit id="submit-method">
            <source>Méthode de soumission de documents déclencheurs</source>
         </trans-unit>
         <trans-unit id="submit-queue">
            <source>file d'attente (utiliser le cache)</source>
         </trans-unit>
         <trans-unit id="submit-direct">
            <source>directe (éviter le cache)</source>
         </trans-unit>
         <trans-unit id="start-driver">
            <source>Démarrer le pilote au besoin</source>
         </trans-unit>
         <trans-unit id="stop-driver">
            <source>Arrêter le pilote à la fin du traitement des déclencheurs</source>
         </trans-unit>
      </body>
   </file>
   </xliff>
   <xliff version="1.0">
      <file datatype="xliff" original="Trigger.xml" source-language="ja" xml:space="preserve">
      <header/>
      <body>
         <trans-unit id="job-display-name">
            <source>購読者チャネルトリガ</source>
         </trans-unit>
         <trans-unit id="job-description">
            <source>このジョブはゼロまたは複数のトリガドキュメントを購読者チャネルに送信します。この送信はスコープが定義されている場合はオブジェクトごとに1つのドキュメント、または各ジョブ実行に対して1つのドキュメントとなります。</source>
         </trans-unit>
         <trans-unit id="process-unassociated">
            <source>ドライバの関連付けなしでオブジェクトに対するトリガドキュメントを送信しますか?</source>
         </trans-unit>
         <trans-unit id="use-job-cn">
            <source>ジョブCNをトリガドキュメント識別子として使用しますか?</source>
         </trans-unit>
         <trans-unit id="use-job-cn-desc">
            <source>設定されている場合、ジョブオブジェクトのCNをトリガ要素の「ソース」属性の値として使用します。</source>
         </trans-unit>
         <trans-unit id="trigger-source">
            <source>トリガ要素ソース値</source>
         </trans-unit>
         <trans-unit id="trigger-source-desc">
            <source>トリガ要素の「ソース」属性の値として使用する文字列です。</source>
         </trans-unit>
         <trans-unit id="submit-method">
            <source>トリガドキュメントを送信するための方法</source>
         </trans-unit>
         <trans-unit id="submit-queue">
            <source>キュー(キャッシュの使用)</source>
         </trans-unit>
         <trans-unit id="submit-direct">
            <source>直接(バイパスキャッシュ)</source>
         </trans-unit>
         <trans-unit id="start-driver">
            <source>実行していない場合ドライバを開始する</source>
         </trans-unit>
         <trans-unit id="stop-driver">
            <source>トリガの処理が終了した場合ドライバを停止する</source>
         </trans-unit>
      </body>
   </file>
   </xliff>
   <xliff version="1.0">
      <file datatype="xliff" original="Trigger.xml" source-language="zh_CN" xml:space="preserve">
      <header/>
      <body>
         <trans-unit id="job-display-name">
            <source>订购者通道触发器</source>
         </trans-unit>
         <trans-unit id="job-description">
            <source>该作业将向订购者通道提交 0 个或多个触发器文档。如果定义了范围,可以对每个对象提交一个文档,也可以对每次作业运行提交一个文档。</source>
         </trans-unit>
         <trans-unit id="process-unassociated">
            <source>为没有驱动程序关联的对象提交触发器文档吗?</source>
         </trans-unit>
         <trans-unit id="use-job-cn">
            <source>将“作业 CN”用作触发器文档的标识符吗?</source>
         </trans-unit>
         <trans-unit id="use-job-cn-desc">
            <source>如果设置,将把该作业对象的 CN 用作触发器元素的“源”特性的值。</source>
         </trans-unit>
         <trans-unit id="trigger-source">
            <source>触发器元素源值</source>
         </trans-unit>
         <trans-unit id="trigger-source-desc">
            <source>用作触发器元素“源”特性的值的字符串。</source>
         </trans-unit>
         <trans-unit id="submit-method">
            <source>提交触发器文档的方式</source>
         </trans-unit>
         <trans-unit id="submit-queue">
            <source>队列(使用超速缓存)</source>
         </trans-unit>
         <trans-unit id="submit-direct">
            <source>直接(绕过超速缓存)</source>
         </trans-unit>
         <trans-unit id="start-driver">
            <source>启动驱动程序(如果未运行)</source>
         </trans-unit>
         <trans-unit id="stop-driver">
            <source>完成对触发器的处理时停止驱动程序</source>
         </trans-unit>
      </body>
   </file>
   </xliff>
   <xliff version="1.0">
      <file datatype="xliff" original="Trigger.xml" source-language="zh_TW" xml:space="preserve">
      <header/>
      <body>
         <trans-unit id="job-display-name">
            <source>訂閱者通道觸發程式</source>
         </trans-unit>
         <trans-unit id="job-description">
            <source>此工作會提交零個以上的觸發文件至訂閱者通道。此作業可能每一個物件提交一份文件 (若已定義範圍) 或可能每一個工作提交單一份文件。</source>
         </trans-unit>
         <trans-unit id="process-unassociated">
            <source>是否要為沒有驅動程式關聯的物件提交觸發文件?</source>
         </trans-unit>
         <trans-unit id="use-job-cn">
            <source>是否要以「工作 CN」 做為觸發文件識別碼?</source>
         </trans-unit>
         <trans-unit id="use-job-cn-desc">
            <source>如果設定,則請以工作物件的 CN 做為觸發元素之「來源」屬性值。</source>
         </trans-unit>
         <trans-unit id="trigger-source">
            <source>觸發元素來源值</source>
         </trans-unit>
         <trans-unit id="trigger-source-desc">
            <source>字串,做為觸發元素之「來源」屬性值。</source>
         </trans-unit>
         <trans-unit id="submit-method">
            <source>觸發文件的提交方法</source>
         </trans-unit>
         <trans-unit id="submit-queue">
            <source>佇列 (使用快取)</source>
         </trans-unit>
         <trans-unit id="submit-direct">
            <source>直接 (不使用快取)</source>
         </trans-unit>
         <trans-unit id="start-driver">
            <source>啟動驅動程式 (若未執行)</source>
         </trans-unit>
         <trans-unit id="stop-driver">
            <source>當完成處理觸發程式時停止驅動程式</source>
         </trans-unit>
      </body>
   </file>
   </xliff>
</job-aggregation>

Conclusion

Once implemented, this driver provides a mechanism to update the Login Time attribute in the Identity Vault to accurately reflect the latest login time across all authentication domains. In a future article, we will discuss a driver to report on a scheduled basis data about the users via HTML eMail generated by IDM.

AttachmentSize
using_the_scripting_driver_to_query_active_directory.doc241.5 KB

New Storage Manager 3.0.1 Podcast Posted

$
0
0

The Novell Open Audio team recently conducted an in-depth interview with with David Condrey and Sophia Germanides on the product enhancements added to Novell Storage Manager 3, including those recently introduced in Novell Storage Manager 3.0.1.

Some of the topics discussed in the new podcast include:

• Feature parity between the eDirectory and Active Directory versions of the product
• How Novell Storage Manager works with products from NetApp, EMC, and HP
• Storage redistribution and load balancing
• How Novell Storage Manager controls costs associated with the largest line item in a CEO's budget—storage
• How Novell Storage Manager addresses compliance and information governance challenges
• Cross-Empire Data Migration

The podcast is about 30 minutes and is available here: http://www.novell.com/recording/novellopenaudio/20...


Kerberos Single Sign-on with Passwords through Access Manager

$
0
0

The new PasswordFetch class offers the ability to retrieve passwords from eDirectory when they are not supplied via the original authentication Method. The workflow diagram shows how PasswordFetch sits in the architecture of the solution:

Environment

In this setup example, the following lists the environment configuration:

  • SLES 11 (lnxwlgmulti01.directoryconcepts.co.nz - 192.168.10.9)
  • eDirectory 8.8.6 (t=DCNZ)
  • Universal Password Enabled
  • Objects under container: o=IDENTITIES
  • Apache running for office.directoryconcepts.co.nz with SSL enabled (signed by DCNZ)
  • Windows Server 2008 R2 (w2k8.ad.directoryconcepts.co.nz - 192.168.10.11)
  • Active Directory (dc=ad,dc=directoryconcepts,dc=co,dc=nz)
  • Domain Controller Server Certificate signed by DCNZ for IDM purposes and AD LDAPS enablement
  • Objects under container: ou=BASEDN,dc=ad,dc=directoryconcepts,dc=co,dc=nz
  • Novell Access Manager 3.1.3-247
  • Administration Console & Identity Provider on SLES 11 x64 (idp.ad.directoryconcepts.co.nz - 192.168.10.4)
  • Linux Access Gateway (lag.ad.directoryconcepts.co.nz - 192.168.10.7)
  • The LAG protects the iDP, so the Base URL is https://office.directoryconcepts.co.nz:443/nidp
  • Windows 7 x64 SP1
  • Active Directory Member
  • Internet Explorer 9
  • DCNZ Self Signed Certificate Authority imported into Computer Trusted Roots Store
  • *.directoryconcepts.co.nz added to IE's Local Intranet sites

User Stores

Active Directory

Create the User for Kerberos (we will also use this user for AM3 Store Access, so give it Domain Admin membership).

The User Login Name must be HTTP/ followed by the Access Manager Base URL domain (i.e. HTTP/office.directoryconcepts.co.nz)

For this next screen, you will need to enable Advanced Features in MMC.

Now we need to export the Kerberos Key Tab file:

eDirectory

Create the test user in ou=ACTIVE,o=IDENTITIES and allow IDM to synchronise the user to Active Directory. This will result in:

  • eDirectory Account: cn=bwalter,ou=ACTIVE,o=IDENTITIES
  • Active Directory Account: CN=Ben Walter,OU=Users,OU=BASEDN,dc=ad,dc=directoryconcepts,dc=co,dc=nz

IDM will write back the DirXML-ADContext value of CN=Ben Walter,OU=Users,OU=BASEDN,dc=ad,dc=directoryconcepts,dc=co,dc=nz to the cn=bwalter,ou=ACTIVE,o=IDETNTIES object (we will use this attribute as part of the PasswordFetch class).

Access Manager iDP

First, we create the primary store which will be Active Directory.

Next, we need to create the eDirectory store for the PasswordFetch class to use.

Next, we create the Kerberos Class called "Kerberos". In the Properties, we add the following values to match with Active Directory:

The bcsLogin.conf looks like:

com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule required
debug="true"
useTicketCache="true"
ticketCache="/opt/novell/java/jre/lib/security/spnegoTicket.cache"
doNotPrompt="true"
principal="HTTP/office.directoryconcepts.co.nz@AD.DIRECTORYCONCEPTS.CO.NZ"
useKeyTab="true"
keyTab="/opt/novell/java/jre/lib/security/nidpkey.keytab"
storeKey="true";
};

While here, we also create the PasswordFetch Class called "PasswordFetch". In the Properties, we add the following values:

So, when the Kerberos Token is received by the iDP from the client, it validates with Active Directory (based on userPrincipalName in the token - bwalter@AD.DIRECTORYCONCEPTS.CO.NZ), this returns the DN of the user object (CN=Ben Walter,OU=Users,OU=BASEDN,dc=ad,dc=directoryconcepts,dc=co,dc=nz). To find the equivalent user in eDirectory, we use this to search for them from the DirXML-ADContext value.

11:20:29 A122EB70 LDAP: (192.168.10.4:50259)(0x0002:0x63) Search request:
   base: "o=IDENTITIES"
   scope:2 dereference:3 sizelimit:0 timelimit:0 attrsonly:0
   filter: "(&(objectClass=Person)(DirXML-ADContext=CN=Ben Walter,OU=Users,ou=BASEDN,dc=ad,dc=directoryconcepts,dc=co,dc=nz))"
   attribute: "GUID"
   attribute: "fullname"

For each Class, we create a Method:

The Kerberos Method uses the Active Directory Store.

The PasswordFetch Method uses the eDirectory Store.

The options to Overwrite User values means that the LDAP DN will be that of eDirectory, not Active Directory (should it be needed).

Now we create the Contract to be utilised:

Policies

Because we told the PasswordFetch Method to overwrite the user values, any policy should use the eDirectory values for LDAP. i.e.:

Internal/External Scenario

Consider the situation where your business has one Access Manager solution for both Internal and External resources. The Internal resources utilise the method described above for single sign on, but what about External Users? They have no Kerberos token to send through!

What we need to do is provide a "fallback" contract for External Users so they are prompted to authenticate when no Kerberos Token is received. First we configure the Kerberos Contract with an Authentication Level, etc

Then we configure our "fallback" contract, in this case the Secure Username/Password Form:

Then we disable the Authentication Cards for all other unused Contracts to make the Secure Username/Password Form the default:

The Catch! The iDP still needs an authentication header passed to the Kerberos Contract, even if its empty, otherwise it errors requiring authentication. To do this, the external clients must be configured like internal clients (i.e. setting Internet Explorer to enable Integrated Login and add the domain to the Intranet Sites, or setting the network.negotiate-auth.delegation-uris and network.negotiate-auth.trusted-uris in Firefox).

Alternate

As of version 3.1.4 of Novell Access Manager, new capabilities have been added to the Kerberos Class. These are documented.

Kerberos Method Properties Example:

  • FALLBACK_AUTHCLASS: com.novell.nidp.authentication.local.ProtectedPasswordClass
  • MainJSP: true
  • JSP: custom_page

When the Active Directory driver will not synchronize attributes

$
0
0

Recently I was working on an Active Directory driver, and the task we had was to add regular users from the Identity Vault into a new Active Directory domain as Contact object. The notional use case here was to get all the current users into the Exchange address book.

Well it turns out there were a couple of really interesting issues we encountered along the way that are worth discussing, and hopefully will be useful to others.

While we were working in our development lab we did not yet have an actual Exchange server added, we just had an Active Directory domain with some users in it. Since they also wanted the existing users to match and synchronize changes after the match, we needed to handle users properly as well.

None of this is very profound, and the direction we took was to handle users normally, and if we noticed the object was missing an entitlement flag, we would then make a Contact object in Active Directory for it. This was easy to do with a simple use of the Set Operation Class token in Policy Builder, which is actually a pretty cool trick, if you are not aware of it.

This token will change the current class of the operation. You could probably do this in XPATH with a set XML attribute of class-name with the new value, but it is much easier to read using the token meant for the task. I am actually not sure if there are any other consequences or differences between the two approaches, but it seems like built in function is the more appropriate way to handle it.

This was all going great, with the usual minor quirks that come with morphing an objects class (User in eDirectory to Contact in Active Directory for unflagged users) until we got an actual Exchange system installed.

We had migrated in a few dozen Users from the Identity Vault, that became Contacts in Active Directory and they were not showing up in Exchange. Well at least not in the Outlook address book, as we desired.

Digging around, since this was Exchange 2003, in the Exchange Server manager MMC (Microsoft Management Console. Which by the way, if you run mmc.exe on Windows, look at the title bar of the window, it says Console1, you will laugh...) we found that the Address book listings are actually defined by an LDAP query. This is true for "All Contacts" and the other types of Address Book options that show up in Outlook and Outlook Web Access.

Now I like to think I can read LDAP filters quite well, but this one was a mouthful. Here it is, as shown in the MMC console, lets see if you can make heads or tails of this one:

(&(mailnickname=*)(|(&(objectCategory=person)(objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=user)(|(homeMDB=*)(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=contact))(objectCategory=group)(objectCategory=publicFolder)(objectCategory=msExchDynamicDistributionList)))

However, I knew that a good tool that knows how to handle LDAP filters could parse this into a more human readable format. Now for LDAP operations, I happen to prefer LBE, LDAP Browser and Editor by a guy with a crazy name of Gawor. He used to work at one of the US National Labs (Lawrence Livermore I think) but finally grew up and got a real job, at which point they stopped hosting his client. The good news is you can get it at this link: Jarek Gawor's excellent LDAP Browser/Editor v2.8.2

This is a 600K file, that is small enough to keep in your email for when you need it, and it is Java based, and the same JAR file runs on a Mac, Windows, or Linux box no problems. This is a great tool, since it is tiny, fast, and completely cross platform. However, it has not been updated in something like eight or nine years, and has some minor quirks.

The other major LDAP tools out there seem to be Softerra's LDAP Browser which I am ambivalent about, as they went commercial and to my mind it is not great enough to be worth paying for. The other major player seems to be Apache Directory Studio tool (ApacheDS). You can get this at: http://directory.apache.org/

ApacheDS is Eclipse based, like Designer and Analyzer, so it too is cross platform as long as you have Java, however the foot print for Eclipse versus LBE is night and day in terms of memory and CPU.

Nonetheless ApachDS as at least two unique features for which I will load it up and use it. The first is instead of exporting LDAP objects from a directory in LDIF format files, it can also export into CSV (Comma Separated Values) text files, which is awesome if you want to manipulate it in Excel to clean up or rename some of the data. It also claims to export straight to Excel format, but I have never had good luck with that function. Since CSV can be imported directly into Excel in a moments effort, I rarely care.

The second key feature that is less obvious that is very powerful is that the LDAP filter builder has an awesome parser to show you the filter in a much more readable fashion.

So I took that three hundred and thirty three character long LDAP filter from Exchanges Address Book configuration and looked at it in ApacheDS's LDAP filter builder, and it was displayed in a much more easily understood format:

(&
    (mailnickname=*)
    (|
        (&
            (objectCategory=person)
            (objectClass=user)
            (!(homeMDB=*))
            (!(msExchHomeServerName=*))
        )
        (&
            (objectCategory=person)
            (objectClass=user)
            (|
                (homeMDB=*)
                (msExchHomeServerName=*)
            )
        )
        (&
            (objectCategory=person)
            (objectClass=contact)
        )
        (objectCategory=group)
        (objectCategory=publicFolder)
        (objectCategory=msExchDynamicDistributionList)
    )
)

Now that I see it this way it is pretty easy to see that the filter is looking for objects with a value in the mailNickname attribute that are either a User with some of the Exchange attributes populated, a contact object, a Group, a Public folder, or an msExchDynamicDistributionList object.

That is, there first & sign, is ANDing together the mailNickname = * (i.e. has a value for mailNickname) with the next predicate, which is a bunch of ORed (| sign indicates OR in LDAP filters) conditions. Within the OR group, the first three have some ANDed (&) values. That is, it should be a person, a User without a homeMDB value, and without a msExchHomeServerName value since I guess users with Exchange accounts are already in the list via some other fashion.

You can see there are six different objects (or objects with conditions) that are OR'ed together. As you can see, much much easier to read when broken down this way. I tried doing it by hand, and it did not work anywhere near as well!

But the key was that we were not populating mailNickname on the Contact objects, thus they never would show up in the Exchange address book.

Of course it turns out there was more too it than that. So now that we had Exchange installed, we went into the Exchange Server Manager MMC and made a Contact object and compared the Contact object we made via IDM with the Contact object made via the Exchange MMC, and we found a couple of extra attributes we missed. Specifically we found we should add a proxyAddresses attribute (which I actually should have remembered from another client, hey Eric P!) that is multivalued, and strangely has the formatting of SMTP:user@domain.com.

The other is targetAddress that is single valued and has the same odd formatting. This is how Exchange knows which protocol to send email via, since in theory it looks like it still support X.400. Anyone remember X.400 for email? Like X.500 for directory services, it seemed like a good idea at the time, but in the end, no one really ever used it with all the stuff it needed, thus the L in LDAP for lightweight, since the full X.500 DAP was really really a heavy weight (Watch out Evander Holyfield, X.500 DAP could take you out in a second!). Seems like only IBM ever implemented anything that used all of X.500 (DCE anyone?) or X.400. Heck or even the full seven layer OSI model. SNA was one of the few protocols to actually use all seven layers the way they were meant too.

Well fixing this in DirXML Script is a piece of cake, since adding new attributes, and managing them when things change is exactly what it is designed to do.

The funny part is of course that the Exchange server got installed on the Thursday afternoon right before one of the holiday weekends (Thanks Jeff for the timing!) so of course everyone wanted to go home, and I really wanted to just demonstrate this worked before the holiday. So I was in a rush to implement this, and it was interesting how fast it really happened.

Anyway, so off we go, and tested with a modify, and awesome, the users are showing up in the Address book! Yay!

To be complete, went and did a quick migrate of some other user from the Identity Vault. To make it more challenging, I was on a call with someone discussing the project, while I tried to get this done, and I could not focus well enough to remember what the Create rule policies were, in terms of required attributes. So it took me four tries, each time Operation Vetoed by the Create rule. Fun trying to split your attention.

When I finally got a valid user, I was happy, did a quick look at it in LDAP and everything looked good, except I noticed that mailNickname was missed. What? I know it was sent. Why was it not in Active Directory? And it worked on a modify. What was going on? Well of course I was looking in Dstrace all along to see what was going on. When I looked on the engine side of the trace, I saw an <add> event go through with the correct set of attributes including mailNickname. But Active Directory was not showing it. Next place to look is on the Remote loader trace to see what the shim was doing with the attributes, so lets look at the Remote Loader trace:

DirXML: [12/30/10 17:32:09.76]: 
<nds dtdversion="3.5" ndsversion="8.x">
	<source>
		<product version="3.6.10.4747">DirXML</product>
		<contact>Novell, Inc.</contact>
	</source>
	<input>
		<add class-name="contact" dest-dn="cn=RSMITH7,ou=Contacts,dc=acme,dc=local" event-id="acmedidv3#20101230223222#3#1" qualified-src-dn="O=idv\OU=Users\CN=RSMITH7" src-dn="\acmeIDV\idv\Users\RSMITH7" src-entry-id="214293">
			<add-attr attr-name="displayName">
				<value timestamp="1256696225#32" type="string">Ryan Smith</value>
			</add-attr>
			<add-attr attr-name="givenName">
				<value timestamp="1256696220#26" type="string">Ryan</value>
			</add-attr>
			<add-attr attr-name="dirxml-uACAccountDisable">
				<value timestamp="1293747133#12" type="state">false</value>
			</add-attr>
			<add-attr attr-name="sn">
				<value timestamp="1256696220#27" type="string">Smith</value>
			</add-attr>
			<add-attr attr-name="telephoneNumber">
				<value timestamp="1256696220#16" type="teleNumber">NA</value>
			</add-attr>
			<add-attr attr-name="title">
				<value timestamp="1256696220#24" type="string">User Administrator</value>
			</add-attr>
			<add-attr attr-name="cn">
				<value>RSMITH7</value>
			</add-attr>
			<add-attr attr-name="mail">
				<value type="string">RSMITH7@acmemail.com</value>
			</add-attr>
			<add-attr attr-name="mailNickname">
				<value type="string">RSMITH7</value>
			</add-attr>
			<add-attr attr-name="proxyAddresses">
				<value type="string">SMTP:RSMITH7@acmemail.com</value>
			</add-attr>
			<add-attr attr-name="targetAddress">
				<value type="string">SMTP:RSMITH7@acmemail.com</value>
			</add-attr>
		</add>
	</input>
</nds>

Hmm, there it is, mailNickname in the <add> event in the snippet:

<add-attr attr-name="mailNickname">
	<value type="string">RSMITH7</value>
</add-attr>

Looking good so far. Then the ADdriver.dll shim parses the XDS it received, as the trace shows below:

DirXML: [12/30/10 17:32:09.76]: ADDriver: parse command

  className    contact
  destDN       cn=RSMITH7,ou=Contacts,dc=acme,dc=local
  eventId      acmedidv3#20101230223222#3#1
  association  
DirXML: [12/30/10 17:32:09.76]: ADDriver: MadCommandAdd::onCommand
DirXML: [12/30/10 17:32:09.76]: ADDriver: Imported attribute notes
   MAD syntax     DirectoryString (2.5.5.12,64)
   XDS syntax     string
   Single valued  true
   Case sensitive false
DirXML: [12/30/10 17:32:09.76]: ADDriver: Imported attribute cn
   MAD syntax     DirectoryString (2.5.5.12,64)
   XDS syntax     string
   Single valued  true
   Case sensitive false
DirXML: [12/30/10 17:32:09.76]: ADDriver: Imported class contact
DirXML: [12/30/10 17:32:09.76]: ADDriver: MadCommandAdd::insertXdsAttributes()
DirXML: [12/30/10 17:32:09.76]: ADDriver: displayName
DirXML: [12/30/10 17:32:09.76]: ADDriver: givenName
DirXML: [12/30/10 17:32:09.76]: ADDriver: dirxml-uACAccountDisable
DirXML: [12/30/10 17:32:09.76]: ADDriver: sn
DirXML: [12/30/10 17:32:09.76]: ADDriver: telephoneNumber
DirXML: [12/30/10 17:32:09.76]: ADDriver: title
DirXML: [12/30/10 17:32:09.76]: ADDriver: cn
DirXML: [12/30/10 17:32:09.76]: ADDriver: mail
DirXML: [12/30/10 17:32:09.76]: ADDriver: mailNickname
DirXML: [12/30/10 17:32:09.76]: ADDriver: Imported attribute mailNickname
   MAD syntax     DirectoryString (2.5.5.12,64)
   XDS syntax     string
   Single valued  true
   Case sensitive false
DirXML: [12/30/10 17:32:09.76]: ADDriver: proxyAddresses
DirXML: [12/30/10 17:32:09.76]: ADDriver: Imported attribute proxyAddresses
   MAD syntax     DirectoryString (2.5.5.12,64)
   XDS syntax     string
   Single valued  false
   Case sensitive false
DirXML: [12/30/10 17:32:09.76]: ADDriver: targetAddress
DirXML: [12/30/10 17:32:09.76]: ADDriver: Imported attribute targetAddress
   MAD syntax     DirectoryString (2.5.5.12,64)
   XDS syntax     string
   Single valued  true
   Case sensitive false
   
   

Looking through that, we see for a couple of attributes it had to import attributes (I am not sure exactly what that means, and why for some and not for others, maybe since they are not directly in the filter, the shim does not load them out of AD schema at driver start time) and it shows that it found mailNickname, and correctly read back its schema syntax from Active Directory with this trace snippet:

DirXML: [12/30/10 17:32:09.76]: ADDriver: mailNickname
DirXML: [12/30/10 17:32:09.76]: ADDriver: Imported attribute mailNickname
   MAD syntax     DirectoryString (2.5.5.12,64)
   XDS syntax     string
   Single valued  true
   Case sensitive false
   
   

Ok, it is not case sensitive, it is a DirectoryString, which means pretty much Case Ignore String in eDirectory, which is good, and it is single valued. This all looks great so far.

Next the Remote Loader converts the XDS event to a shim event (basically an LDAP modify) as the trace shows:

DirXML: [12/30/10 17:32:09.76]: ADDriver: Add contact cn=RSMITH7,ou=Contacts,dc=acme,dc=local
LDAPMod operations:
add attribute objectClass
>> contact
add attribute objectCategory
>> CN=Person,CN=Schema,CN=Configuration,DC=acme,DC=local
add attribute displayName
>> Ryan Smith
add attribute givenName
>> Ryan
add attribute sn
>> Smith
add attribute telephoneNumber
>> NA
add attribute title
>> User Administrator
add attribute cn
>> RSMITH7
add attribute mail
>> RSMITH7@acmemail.com
add attribute proxyAddresses
>> SMTP:RSMITH7@acmemail.com
add attribute targetAddress
>> SMTP:RSMITH7@acmemail.com
DirXML: [12/30/10 17:32:09.78]: ADDriver: set userAccountControl returns 0x0041
DirXML: [12/30/10 17:32:09.78]: Loader: subscriptionShim->execute() returned:
DirXML: [12/30/10 17:32:09.78]: Loader: XML Document:
DirXML: [12/30/10 17:32:09.78]: <nds ndsversion="8.7" dtdversion="1.1">
	<source>
		<product version="3.5.10" asn1id="" build="20100709_120000" instance="\acmeIDV\idv\servers\IDMDriverSet\BresnanAD">AD</product>
		<contact>Novell, Inc.</contact>
	</source>
	<output>
		<add-association dest-dn="\acmeIDV\idv\Users\RSMITH7" dest-entry-id="214293" event-id="acmedidv3#20101230223222#3#1">b1c74114f57e9c4aa2d102343ac74cea</add-association>
		<status level="success" event-id="acmedidv3#20101230223222#3#1"/>
	</output>
</nds>
DirXML: [12/30/10 17:32:09.78]: 
DirXML Log Event -------------------
    Driver  = \acmeIDV\idv\servers\IDMDriverSet\BresnanAD
    Thread  = Subscriber Channel
    Object  = \acmeIDV\idv\Users\RSMITH7 (cn=RSMITH7,ou=Contacts,dc=acme,dc=local)
    Level   = success
	
	

Ok, so we had a successful add of a contact object, the shim sent back an <add-association> event, but wow nellie! Where did mailNickname go? It was there a second ago. It let proxyAddresses and targetAddress through. Why did it filter out mailNickname?

It turns out I have a lousy memory for fine detail. But I have a pretty good memory for high level stuff, and while I read a lot, I do not remember a lot of details, but I do know I can use the magic of Google to augment my fine detail memory. A quick search turned up a thread in the forums, that I had even commented on a couple of times,. that I vaguely remembered at a high level, but could not remember any of the fine details. Well my Google memory crutch worked again! (Actually, this is why I started writing for Cool Solutions. I would fix something at work, and then hit it again a month or three later and totally forget how to fix it).

http://forums.novell.com/novell-product-support-forums/identity-manager/im-engine-drivers/404302-ad-driver-stripping-my-attribute-post1948543.html

Anyway, it turns out that on an <add> event, the Active Directory driver knows that a couple of attributes are really Exchange attributes, and for Exchange 2007 and 2010, are managed via PowerShell instead of via direct mapping. Therefore it strips them out. The latest (well 3.5.10 version used to be latest, as I write this, 3.5.13 was out already) version of the driver (Which I was actually running) will not strip them out, if you are set to use the default Exchange provisioning model.

Well it turns out, I had Exchange provisioning disabled, but nonetheless the Global Configuration Variable (GCV) that controlled this setting was still set to Exchange 2007 in the configuration I was using. So I had to go enable Exchange provisioning via the GCV, set the type of Exchange server to Default and save it. Then go back, and Disable Exchange provisioning.

Additionally what worked was changing the set destination attribute for mailNickname to be set after the current operation.

That meant that the <add> event would go through, setting almost all the attributes I needed, but it would then be followed by a <modify> event of just a single attribute, mailNickname, which we had already shown worked.

Anyway, this certainly was one of those interesting issues, that is quite complicated at the end, but really just a series of simple steps, if you can break it down. Hope this helps someone down the road when they run into a similar problem, like the forum post helped me out!

Managing multiple Active Directory domains in one IDM system - Part 4

$
0
0

The Active Directory driver in all versions of Identity Manager from 2.0 and up, when we started using DirXML Script instead of XSLT for policies have had a pair of attributes in common. DirXML-ADContext and DirXML-ADAliasName.

These are used to handle the strange circumstance of a rename or move event in Active Directory. The shim cannot tell the difference between a rename or move event. Both cases are actually a modify of the distinguishedName (DN) attribute.

For example: cn=geoffc, ou=Users, dc=acme, dc=com

If this were to change, it would depend where the change occurred to know if this was a move or rename event. If the cn=geoffc part changed, then that is a rename event. If any of the ou=Users, dc=acme, dc=com part changed, then this was a move event, since the parent container for the object is now different, in other words a move event.

You can find the policies that manage this in the Publisher Event Transformation rule set. There have been a number of different shipping default configurations for Identity Manager, so the name of the rule and its exact workings have changed over time. With the advent of IDM 4 and Packages, we should see this stabilize and become more manageable.

The problem with using single valued, simple string attributes for this task is how do you handle a second AD driver in a different domain? Well if the user is only ever in a single domain, that is fine, but if you have worked with IDM then you know that one of the cool things about IDM is how easy it would be to manage users in multiple AD domains, eDirectory, and many other connected services.

If you add a second Active Directory domain to your IDM world, then the values of DirXML-ADAliasName and DirXML-ADContext will be incompatible between drivers and domains.

In the first article in this series Managing multiple Active Directory domains in one IDM system - Part 1 I discussed an approach in general of a plural version of the attributes, using Path syntax.

In the second article in this series Managing multiple Active Directory domains in one IDM system - Part 2 I discussed more of the specific implementation details of making this work. I provided some sample code for making sure you update the attributes correctly.

In the third article in this series Managing multiple Active Directory domains in one IDM system - Part 3 I worked through a good chunk of the Publisher channel rules that need to changed for the DirXML-ADContexts attribute. Still need to do the same for the DirXML-ADAliasNames attribute.

In this article I will work through the rest of the Publisher channel.

As a quick recap, we will use DirXML-ADContexts and DirXML-ADAliasNames (note the plural) as multi valued, path syntax attributes instead of the single valued string equivalents that IDM ships with. In the previous article I showed some sample code on how you might update that attribute. The key is to remember to find the current value associated with this driver, remove it, then add a new value in.

Next up, is to decide WHERE do we need to make these changes? Well the easy way to manage this is to export the driver configuration as an iManager configuration file, open it in a text editor and then search for the various attribute names.

I wanted to use the latest configuration, since the IDM 3.6.1 has several possible configurations. (V4, V5, V6, and with one of the patches V7). However I happen to know that there are a number of bugs fixed in the IDM 4 Package version of the driver. I was starting a new project that needed an Active Directory driver but could not yet use IDM 4 for this project, instead had to use IDM 3.6.1. So I decided to try something funny.

I started a scratchpad Designer project, set up an IDM 4 Identity vault, added the Active Directory packaged driver, with all the options, and then I configured it for the most part.

Next I exported the driver to an iManager configuration file, and edited that, making it work with IDM 3.6.1. I have to find the time to write up that exercise as that was quite interesting to see how Packages have changed some things in the config files.

Then I searched through to find all the locations these attributes are in use. Here are my results, shown for the two different attributes, in case you want to identify them independently.

DirXML-ADContexts plural:

Input Transform:
     NOVLADDCFG-itp-SubscriberUserAdd 1 rule

Publisher Channel:
     Event Transformation:
          NOVLADDCFG-pub-etp-HandleMovesAndRenames 3 rules

     Create Policy Set:
          NOVLADDCFG-pub-cp 1 rule

     Command Transform:
          NOVLADDCFG-pub-ctp 2 rules

Subscriber Channel:
     Command Transform:
          NOVLADENTEX-sub-ctp-EntitlementsImpl 2 rules (more?)

DirXML-ADAliasNames:

Filter (done)
Schema Map (done)

Subscriber Channel:
     Create Policy Set:
          NOVLADDCFG-sub-cp-Users, 1 rule
          NOVLADDCFG-sub-cp-Groups 1 rule

     Command Transform:
          NOVLADENTEX-sub-ctp-EntitlementsImpl 2 or more rules)

Publisher Channel:
     Command Transform:
          NOVLADDCFG-pub-ctp 2 rules
          NOVLADDCFG-pub-ctp-UserNameMap 5 rules

New rules needed:
     Input Transform
          itp-Convert Plural Attribute

     Output Transform
          otp-Convert Plural Attribute

We are working through the changes needed for the DirXML-ADContexts attribute and in the Publisher channel, up to the Command Transform.

In the Policy object pub-ctp (part of the NOVLADDCFG package) there is one more rule that needs updating.

set cached context value on merge

Now this rule puzzles me. It looks like there is a bug in the configuration, or else I do not understand what is going on. From the rule name, since there is no Comment to explain, one would assume on a merge event, then this rule should update the cached context value. The cached context value as you will have noted from the previous article is the DirXML-ADContext attribute.

This makes sense. When you either migrate an object, or else make a change on it once the driver is running and the object is not associated, then it will take that event (<sync> or <modify>) and convert it to a synthetic add event. The first thing that happens on a synthetic add is an attempt to match. If it matches, then the two objects are merged together. You also get an XML attribute from-merge with a value of true in the <add from-merge='true'> event node.

Now the condition test for this rule is if XPATH is NOT true @from-merge='true' then it would fire, which seems backwards to me. I have switched it to true, so I am not sure what the story here is really. I think I am correct, but not sure if there are unintended consequences to this rule.

Anyway, inside the rule, there was an add destination attribute DirXML-ADContext action. Well we just need to replace that with the structured attribute add destination attribute that the last few articles have been talking about. Again I think it worthwhile to do a loop and read back the current DirXML-ADContexts, compare for any for this driver, and if found remove them. This is important as there are easy error cases to get into where you might have more than one value, if you did not have this loop present. The simplest example is if you decided to redo a test with a user and remembered to remove the DirXML-Associations value for the driver, but forgot to clean up the DirXML-ADContexts attribute. By doing the check here, that gets cleaned up nicely.

That completes the Publisher channel, now on to the Subscriber channel. There is only one Policy object of interest and it is in the Command Transform named sub-ctp-EntitlementsImpl (part of the NOVLADENTEX package) and it has a couple of rules that need updates.

User Account Entitlement change (Delete Option)
User Account Entitlement change (Disable Option)

These rules are used when you have enabled (via a Global Configuration Value (GCV) setting) the use of entitlements. You should be using entitlements for IDM now a days, mostly because the Roles Based Provisioning Module (RBPM) needs them to really be useful. Basically an entitlement is something granted to an object, that entitles them to use something. (as the name would suggest). For a variety of reasons, in RBPM, there is an abstraction called Resources, that mostly map one to one to Entitlements, mostly for naming and prettiness issues. A friend summarized it nicely (Hey Mike W!) that entitlements are for computers, resources are for people. (I think he is a computer chauvinist, but what else is new in technology jobs?). Anyway, a Role is composed of a bunch of other Roles and/or Resources. But if you do not have Entitlements, there is not a lot you can do with RBPM. So you should be using them.

Anyway, when you add or remove an entitlement, then the driver uses that as the trigger to know to add, delete, or disable the User. In the Active Directory driver, when the Entitlement is removed, the driver configuration offers two choices, delete or disable the AD user. This makes sense as you might not want to delete them, since getting their SID back in AD is hard, and thus any rights they might have had, or rights granted across trusts, so it is better to disable until you are sure you really want to delete them.

Thus the two rules handle these two cases. Now these rules are somewhat different than the previous rules, since they use the Clear Source attribute token, which removes all values in the source (Identity Vault, since we are now on the Subscriber channel).

Obviously with a multi valued plural set of attributes, this will not be acceptable. Well easy fix, we already have the code to do it. We just loop through the DirXML-ADContexts values, test to see if each such value is for our current driver, and if so remove it.

The only major change is that we need to change the channel, since the code samples we have were for the Publisher channel, where we were using destination tokens, and now we need to use source tokens. Easy enough to change in the XML Source view, and a bit of a pain in the Policy Builder view. Here is what the code should look like:

<do-for-each>
	<arg-node-set>
		<token-removed-entitlement name="UserAccount"/>
	</arg-node-set>
	<arg-actions>
		<do-delete-dest-object/>
		<do-remove-association when="after">
			<arg-association>
				<token-association/>
			</arg-association>
		</do-remove-association>
		<do-for-each>
			<arg-node-set>
				<token-src-attr name="DirXML-ADAliasNames"/>
			</arg-node-set>
			<arg-actions>
				<do-if>
					<arg-conditions>
						<and>
							<if-xpath op="true">$current-node/component[@name='volume']="~dirxml.auto.driverdn~"</if-xpath>
						</and>
					</arg-conditions>
					<arg-actions>
						<do-remove-src-attr-value name="DirXML-ADAliasNames">
							<arg-value type="structured">
								<arg-component name="nameSpace">
									<token-xpath expression='$current-node/component[@name="nameSpace"]'/>
								</arg-component>
								<arg-component name="volume">
									<token-xpath expression='$current-node/component[@name="volume"]'/>
								</arg-component>
								<arg-component name="path">
									<token-xpath expression='$current-node/component[@name="path"]'/>
								</arg-component>
							</arg-value>
						</do-remove-src-attr-value>
					</arg-actions>
					<arg-actions/>
				</do-if>
			</arg-actions>
		</do-for-each>
		<do-for-each>
			<arg-node-set>
				<token-src-attr name="DirXML-ADContexts"/>
			</arg-node-set>
			<arg-actions>
				<do-if>
					<arg-conditions>
						<and>
							<if-xpath op="true">$current-node/component[@name='volume']="~dirxml.auto.driverdn~"</if-xpath>
						</and>
					</arg-conditions>
					<arg-actions>
						<do-remove-src-attr-value name="DirXML-ADContexts">
							<arg-value type="structured">
								<arg-component name="nameSpace">
									<token-xpath expression='$current-node/component[@name="nameSpace"]'/>
								</arg-component>
								<arg-component name="volume">
									<token-xpath expression='$current-node/component[@name="volume"]'/>
								</arg-component>
								<arg-component name="path">
									<token-xpath expression='$current-node/component[@name="path"]'/>
								</arg-component>
							</arg-value>
						</do-remove-src-attr-value>
					</arg-actions>
					<arg-actions/>
				</do-if>
			</arg-actions>
		</do-for-each>
	</arg-actions>
</do-for-each>

Initially the rule looped through the Removed Entitlements token results, since it is a little awkward to select them out the raw XML, so a token was added to make it easier, and then did a clear source attribute for DirXML-ADAliasName and DirXML-ADContext and then in the Delete rule, deleted the object in Active Directory, and removed the association value.

This replaces the clear source attribute tokens with a for each loop over the attribute of interest. (Both of our new plural attribute cases are present in both of these rules).

You can see from the XML how easy it is to change the channel in XML, just edit the nodes <token-dest-attr name="DirXML-ADContexts"/> and <do-remove-dest-attr-value> to be <token-src-attr name="DirXML-ADContexts"/> and <do-remove-src-attr-value>. The dest to src switch is really easy, just remember to do the close nodes as well, or else you invalidate the XML and Designer will not let you switch back to the Policy Builder view, nor save it. (Which is really handy since I often forget or make a typo!). Personally I want a button in the user interface to change channels, but then I want a lot of things.

With that rule finished, we have all the known instances of DirXML-ADContext replaced in a compatible fashion with DirXML-ADContexts, the plural version. Now on to the slightly harder example of DirXML-ADAliasNames.

It turns out that while DirXML-ADContext is used for rename and move support, the use for DirXML-ADAliasName has actually changed over time. That is, in earlier driver configurations, DirXML-ADAliasName was mapped to Active Directory's sAMAccountName attribute. This was actually helpful, as CN was multivalued and mapping a multivalued to a single valued attribute can be quite awkward. Additionally, this allows a user to have a different common name in eDirectory than they have in Active Directory which can be helpful at times.

However, with Identity Manager 3.6.1 and the V5 default driver configuration, this mapping changed, from sAMAccountName to DirXML-ADAliasName to instead hold the userPrincipleName value. userPrincipleName (UPN) in Active Directory is a somewhat odd attribute. Usually it holds a name in the format of sAMAccountName@domain.com that kind of looks like an email address, but almost never is a valid email address unless your email domain exactly coincides with the Active Directory domain root. It turns out that Active Directory does not actually validate this value. That means you can actually put any string in there and it will let you, so take care. I know this because we had a client who had a typo and set a couple of thousand users with an incorrect domain value, and no one really noticed. We corrected it with a toolkit rule, like the kind discussed in this series of articles:

Anyway, the change of the mapping was more in depth than just the mapping itself. There are a number of rules that generate the attribute value, and manage it that needed to be modified to support it. I have actually gone through the process of trying to convert the new mapping to be like the old mapping, and it is about as much work as this exercise is, to change how DirXML-ADAliasName and DirXML-ADContext are used.

From the listing of rules affected at the top of the article, we see we have a number of changes needed. We need to update the schema map, since now we are mapping DirXML-ADAliasNames (plural) to userPrincipleName instead of the singular version. Thus we also need to update the Filter, or else the events won't come through.

There are five policy objects that have rules to be changed, and we will work through those, but we also need to add two new policy objects, one in each of the Input and Output transform policy sets. This is because instead of being a simple single valued attribute mapping, we now have a multivalued structured attribute being synchronized with a single valued string attribute, and that just won't work well without some hand holding.

Lets start with the Input and Output transform rules, since these are actually quite simple, with tricky consequences. In fact the Output Transform is a nice simple one liner, that uses the reformat operational attribute token to accomplish everything we need.

You can either take the approach of actually testing for something in the condition block, or not. I prefer to test for if operational attribute is available personally, but when you look at the default policies from Novell you will see they generally do not do any testing, only if they are using the reformat operational attribute token, since of course it does nothing if the attribute is not present in the current document.

Regardless, here is the token as I first used it:

<do-reformat-op-attr name="userPrincipalName">
	<arg-value type="string">
		<token-xpath expression='$current-value/component[@name="path"]/text()'/>
	</arg-value>
</do-reformat-op-attr>

Simple, since we are taking a structured attribute, and flattening it. This takes advantage of the very handy reformat operational attribute token, which is great as it knows how to handle all the different cases where this attribute might come through the driver. That is because a <modify>, <add>, and <instance> event have slightly different XML, so doing this by hand is actually a fair bit harder than it looks. You can read more about the power of this token in this article: Reformat Operation Attribute

Now you will notice there is no testing to be sure this is our instance of the attribute, since we need to make sure that we only send through one attribute change. As I think about it now, I wonder if this will be an issue. For example, once you have two such drivers operating, associating a user to the second driver would probably trigger a change in this driver to the DirXML-ADAliasNames, so I guess we should strip out irrelevant values. Good news is we have the code to do that already in our previous drivers, we just need to change the focus a bit. We could do this by looping through the operational attribute, but after further reflection it seems like a strip by XPATH would be much easier. However, because this could be an <add> or a <modify> event, there are actually at least two different XPATH strings to strip them both. Now I could do this all in one XPATH statement, using OR symbols (pipe or |) but that makes it really hard to read. It turns out to be easier to just do two or three (I include the <instance> case, though it is probably never going to be needed, just to be complete) strip by XPATH tokens to maintain readability over pure elegance.

<do-strip-xpath expression="modify-attr[@attr-name='userPrincipleName']//value[component[@name='volume']!='~dirxml.auto.driverdn~']"/>
<do-strip-xpath expression="add-attr[@attr-name='userPrincipleName']//value[component[@name='volume']!='~dirxml.auto.driverdn~']"/>
<do-strip-xpath expression="attr[@attr-name='userPrincipleName']//value[component[@name='volume']!='~dirxml.auto.driverdn~']"/>

Basically this uses the XPATH of:

attr[@attr-name='userPrincipleName']//value[component[@name='volume']!='~dirxml.auto.driverdn~']"

Differing only in the first node, being either <modify-attr>, <add-attr>, or <attr> nodes in our three possible document cases. In all three, we look for the one where @attr-name = 'userPrincipleName' so we know it is our correct attribute, then knowing it is structured we look for any (the // means any child instance) <value> node where <component name = 'volume'> is NOT equal to our current driver, via the auto GCV. I.e. Strip out everything else. Then do the reformat operation attribute, and we should be good to go.

In the next article we will continue with the counterpart to the Output Transform rule, the Input Transform, that has a subtle twist we need to deal with, then continue working through the rest of the rules that are involved.

Managing multiple Active Directory domains in one IDM system - Part 5

$
0
0

The Active Directory driver in all versions of Identity Manager from 2.0 and up, when we started using DirXML Script instead of XSLT for policies have had a pair of attributes in common. DirXML-ADContext and DirXML-ADAliasName.

These are used to handle the strange circumstance of a rename or move event in Active Directory. The shim cannot tell the difference between a rename or move event. Both cases are actually a modify of the distinguishedName (DN) attribute.

For example: cn=geoffc, ou=Users, dc=acme, dc=com

If this were to change, it would depend where the change occurred to know if this was a move or rename event. If the cn=geoffc part changed, then that is a rename event. If any of the ou=Users, dc=acme, dc=com part changed, then this was a move event, since the parent container for the object is now different, in other words a move event.

You can find the policies that manage this in the Publisher Event Transformation rule set. There have been a number of different shipping default configurations for Identity Manager, so the name of the rule and its exact workings have changed over time. With the advent of IDM 4 and Packages, we should see this stabilize and become more manageable.

The problem with using single valued, simple string attributes for this task is how do you handle a second AD driver in a different domain? Well if the user is only ever in a single domain, that is fine, but if you have worked with IDM then you know that one of the cool things about IDM is how easy it would be to manage users in multiple AD domains, eDirectory, and many other connected services.

If you add a second Active Directory domain to your IDM world, then the values of DirXML-ADAliasName and DirXML-ADContext will be incompatible between drivers and domains.

In the first article in this series Managing multiple Active Directory domains in one IDM system - Part 1 I discussed an approach in general of a plural version of the attributes, using Path syntax.

In the second article in this series Managing multiple Active Directory domains in one IDM system - Part 2 I discussed more of the specific implementation details of making this work. I provided some sample code for making sure you update the attributes correctly.

In the third article in this series Managing multiple Active Directory domains in one IDM system - Part 3 I worked through a good chunk of the Publisher channel rules that need to changed for the DirXML-ADContexts attribute. Still need to do the same for the DirXML-ADAliasNames attribute.

In the fourth article in this series Managing multiple Active Directory domains in one IDM system - Part 4 I worked through the rest of the Publisher channel and started in on the plural DirXML-ADAliasNames that are needed, starting with the Output transform.

As a quick recap, we will use DirXML-ADContexts and DirXML-ADAliasNames (note the plural) as multi valued, path syntax attributes instead of the single valued string equivalents that IDM ships with. In the previous article I showed some sample code on how you might update that attribute. The key is to remember to find the current value associated with this driver, remove it, then add a new value in.

Next up, is to decide WHERE do we need to make these changes? Well the easy way to manage this is to export the driver configuration as an iManager configuration file, open it in a text editor and then search for the various attribute names.

I wanted to use the latest configuration, since the IDM 3.6.1 has several possible configurations. (V4, V5, V6, and with one of the patches V7). However I happen to know that there are a number of bugs fixed in the IDM 4 Package version of the driver. I was starting a new project that needed an Active Directory driver but could not yet use IDM 4 for this project, instead had to use IDM 3.6.1. So I decided to try something funny.

I started a scratchpad Designer project, set up an IDM 4 Identity vault, added the Active Directory packaged driver, with all the options, and then I configured it for the most part.

Next I exported the driver to an iManager configuration file, and edited that, making it work with IDM 3.6.1. I have to find the time to write up that exercise as that was quite interesting to see how Packages have changed some things in the config files.

Then I searched through to find all the locations these attributes are in use. Here are my results, shown for the two different attributes, in case you want to identify them independently.

DirXML-ADContexts plural:

Input Transform:
     NOVLADDCFG-itp-SubscriberUserAdd 1 rule

Publisher Channel:
     Event Transformation:
          NOVLADDCFG-pub-etp-HandleMovesAndRenames 3 rules

     Create Policy Set:
          NOVLADDCFG-pub-cp 1 rule

     Command Transform:
          NOVLADDCFG-pub-ctp 2 rules

Subscriber Channel:
     Command Transform:
          NOVLADENTEX-sub-ctp-EntitlementsImpl 2 rules
          NOVLADDCFG-sub-ctp-UserNameMap 1 rule.

DirXML-ADAliasNames:

Filter (done)
Schema Map (done)

Subscriber Channel:
     Create Policy Set:
          NOVLADDCFG-sub-cp-Users, 1 rule
          NOVLADDCFG-sub-cp-Groups 1 rule

     Command Transform:
          NOVLADENTEX-sub-ctp-EntitlementsImpl 2 or more rules)

Publisher Channel:
     Command Transform:
          NOVLADDCFG-pub-ctp-UserNameMap 2 rules
          NOVLADDCFG-pub-ctp 2 rules

New rules needed:
     Input Transform
          itp-Convert Plural Attribute

     Output Transform
          otp-Convert Plural Attribute

Now on to the Input transform, which has a minor twist that is some what important.

You would think that the Input Transform rule, to convert the simple string attribute coming from Active Directory as User Principle Name to a structured attribute would be the simple converse of the Output transform rule discussed in the previous article. But like most things in life, the devil is in the details.

First off, what should the nameSpace component be? That is a 32 bit integer field, and we are using it to store the time of the event, so I guess we should just decide to use the time in CTIME for the moment the event comes through. Of course, it might be delayed, so should we use the cached time? (Though I am not sure if Active Directory events will have this information, its an interesting idea, hmm, will have to think about that one. I know eDirectory events will come with a cached-time attribute sometimes, wonder if we get it the other way?). Probably the simplest way is to just use NOW as the time.

The path component is the simplest, its the current value of course.

The volume component looks easy, its the current driver DN, or so you would think. If you try it, you will see that the progression into the Event transform will remove it, since the Association value for the driver object is nonexistent. I suppose there are better ways to resolve this, and I am open to them, but I chose the simplest approach. I just set a nonsense association for the driver, ON the driver object. That is, I added by hand, a DirXML-Association value on the driver, with the state of 1 (associated), the DN of the driver itself, and the association value of 1234678 which was as arbitrary as I could come up with at the time. Then I added a GCV to the driver, holding that value I entered by hand. (Did you notice that DirXML-Associations use Path syntax as well? Thus you see a nice analogy of for the plural attributes as well).

Thus the following reformat operational attribute works:

<do-reformat-op-attr name="userPrincipalName">
	<arg-value type="structured">
		<arg-component name="nameSpace">
			<token-time format="!CTIME" tz="UTC"/>
		</arg-component>
		<arg-component name="volume">
			<token-global-variable name="dirxml.auto.driverdn"/>
		</arg-component>
		<arg-component name="path">
			<token-local-variable name="current-value"/>
		</arg-component>
	</arg-value>
</do-reformat-op-attr>

So long as you set an association-ref XML attribute onto the document, for the DN component, which you can do with the set XML attribute token. Oh I guess I could clone by XPATH as well, but since this is an XML attribute I want to add, and there is a built in token, I should use it.

<do-set-xml-attr expression="modify-attr[@attr-name='userPrincipalName']//value/component[@name='volume']" name="association-ref">
	<arg-string>
		<token-global-variable name="driverAssocValue"/>
	</arg-string>
</do-set-xml-attr>

Here you can see the XPATH needed is to identify WHERE to inject this attribute is:

modify-attr[@attr-name='userPrincipalName']//value/component[@name='volume']

That is, in the <modify-attr attr-name='userPrincipalName'> node, find any child values whose <component name='volume'> nodes exist and add it there.

What you should notice is that this only works on modify events. I could not come up with a simple XPATH to handle all three possible cases. The three cases would be a modify, add, or response to a query event. So instead, I just added three set XML attribute actions, since only one will ever succeed.

The other two XPATH examples would be:

Add event:

add-attr[@attr-name='userPrincipalName']//value/component[@name='volume']

Instance doc as a query response:

attr[@attr-name='userPrincipalName']//value/component[@name='volume']

This is part of the magic of the reformat operational attribute, which is smart enough to know how to handle all three of these cases, with just one token. Alas, I do not know how to use the reformat operational attribute to add the needed XML attribute at the same time. Again, if you happen to know how, please comment and let me know!

Now I see two issues with this. In the first case, the Query response is lightly problematic, since you will set the nameSpace component to the current time, whereas the value in eDirectory will always have a value that differs, almost by definition. This means if you ever try to compare the two values they will never meet equality, even if the path value strings are the same. However, this so far does not really matter. But it is worth remembering in case you let the driver merge these two attributes. To my mind, the worst case is that the time will be updated to the current time from whatever value it had in the past, which is something I can live with.

The second thought was that it might make more sense to do this in the Event transform, at which point there is no need for the work around of adding the association-ref value. However, this is a simple enough solution. I am not sure if there is a real downside to adding an association value (nonsense that it may be) to the driver object itself, but that is a back of the mind concern I have with this approach.

Thus we have the reformat operational attribute, and then the three Set XML attribute calls in this rule.

Since we are looking at events coming in, via the Input Transform, lets continue with the Publisher channel. There are two main rules that need changes both in the Publisher Command Transform rule. The first is the pub-ctp-UserNameMap rule, that is part of the NOVLADDCFG package.

There are actually a pair of Username map rules, one in the Publisher channel and one in the Subscriber channel. These exist to manage the mapping of eDirectory's naming attributes to the possible options in Active Directory. As a quick refresher, Active Directory has a number of naming like attributes. They differ from eDirectory, where almost any attribute could be a naming attribute. (It requires a flag in schema for the object class, that this attribute COULD be a naming attribute, and there can be many possible naming attributes in a single object class).

There is sAMAccountName, which is the old NT style NetBIOS name that needs to be unique across the entire domain, regardless of any structure you build inside the domain. Also called the Pre-Windows200 Login name in the MMC view.

Almost always, the User Principle Name is based on the sAMAccountName followed by @domain.com. It technically will accept any value, as there is no back end validation in Active Directory to watch out for this. However, it is best to be correct with proper values. However it could also be your email address, which it looks like, but it does not have to be a valid email address. This needs to be globally unique across the forest (that is conglomeration of trees). On that side note, is it me, or does an Active Directory tree really represent an O in eDirectory, and a forest more closely represent the eDirectory tree root?

To me it seems like something of a semantic distinction. Sort of Orwellian in that you know people did not like having many trees in an enterprise, therefore you say, well now you have a single forest made up of many trees, so there is no issue of many trees, since they are all part of one forest. But I digress...

Then there is the CN of the object. That is, when you consider the full distinguished name of the object in Active Directory, the entire DN needs to be unique, but here you can have duplicate end node values (cn=geoffc in two containers if you like) so long as the entire DN is unique. I.e. No duplication within the same container. This is much like eDirectory's naming model where your naming attribute (whatever it happens to be for this object) must not duplicate another object in the same container's naming attribute. (The first time you see object in DSBrowse using uid (uniqueID) as a naming attribute is when you realize how weird it looks)).

Finally there is displayName, which is what actually shows up in the Active Directory Users and Computers (ADUC) MMC console. Many people will rearrange that to be Last Name, First Name, because it sorts much nicer that way, since it would then sort by last name.

The default Active Directory configuration maps displayName to Full Name in eDirectory. Usually the CN= component is named based on the displayName value, but that is an MMC shortcut doing it for you, since you can actually use whatever you like if you create it low level enough.

The driver supports a number of the naming models here in this Publisher Command Transform rule. The Full Name mapping approach is controlled by a GCV and if selected, the DN of the user to be created in Active Directory will be cn=Full Name Value followed by the rest of the DN. If you change the Full Name attribute in eDirectory, then this is a rename event in Active Directory. Otherwise it uses the eDirectory naming attribute (usually CN for Users, but by no means guaranteed, and since it uses the Source Name token it will use whatever eDirectory is using as a value).

This UserNameMap policy object is dedicated to enforcing the naming decisions you have made via GCV. Additionally it enforces how User Principle Name is managed, when it is mapped to the Active Directory email address attribute, (UpnMap set to ad-mail-auth). The Subscriber channel version of this also has similar mappings.

Here we need to handle two of the possible cases.
     map e-mail address to Active Directory logon name
     unmap e-mail address from Active Directory logon name

These are the cases where UpnMap GCV is set to ad-mail-auth and the Active Directory email is changing. So we need to use the usual loop through the destination attribute DirXML-ADAliasNames, make sure to find any reference to our driver, and remove that one. Then add the correct value based on the changing email address. Then send it back to Active Directory, to the source, remembering that we need to send it as a structured attribute since out Output Transform will strip it back down to a single value.

Then in the unmap email address rule, we basically need to just remove it, since that is handling the case where the email address is just being removed. Use the same approach to handle the destination attribute value, by looping through the values to find the one for this driver, and then remove just that one (or more values). Again as in the previous rule we send a remove source attribute as a structured attribute with all three components so the Output Transform can strip it down as it goes into Active Directory.

Now as it happens, I have never actually used the ad-mail-auth setting for User Principle Name, so I never really paid much attention to these rules, but as I worked through these for this series of articles I started to wonder if the way this policy is laid out would even work properly. So in the defaults before we change it for this project, the two rules are basically the same and adding/removing the email address (Internet EMail Address in eDirectory name space). as the value of the old DirXML-ADAliasName attribute.

The first rule uses the Set token, which issues a remove all values first, then adds a value in one operation. That is fired when the operational attribute Internet EMail Address is available. That means there is an <add-value> or <add-attr> node for this attribute in the event document.

However the second case is if operation attribute Internet EMail Address is changing from the regular expression .+ which means any values. Usually this means it is being removed. This then sends a remove attribute both to the source and destination.

If you had a modify, you might get the remove value for the old value, and an add value for the new value. In that case both the rules would fire, wouldn't they? Then you get two remove values, well one explicit remove value and a remove all values. Though I guess as I think about it, since this is in the Publisher Channel you do not get the nice removed values and add values that you would get out of an event in eDirectory. That is, eDirectory will forward both pieces on a modify, but the Active Directory shim only shows you the new removed value by itself (going to blank or null in Active Directory) or in the case of a modify the new value being added. So this is probably ok in general, even if it looks a bit off.

One more policy object in the Publisher channel and we will get to that in the next article, then on the Subscriber channel and we can wrap this one up.

Managing multiple Active Directory domains in one IDM system - Part 6

$
0
0

The Active Directory driver in all versions of Identity Manager from 2.0 and up, when we started using DirXML Script instead of XSLT for policies have had a pair of attributes in common. DirXML-ADContext and DirXML-ADAliasName.

These are used to handle the strange circumstance of a rename or move event in Active Directory. The shim cannot tell the difference between a rename or move event. Both cases are actually a modify of the distinguishedName (DN) attribute.

For example: cn=geoffc, ou=Users, dc=acme, dc=com

If this were to change, it would depend where the change occurred to know if this was a move or rename event. If the cn=geoffc part changed, then that is a rename event. If any of the ou=Users, dc=acme, dc=com part changed, then this was a move event, since the parent container for the object is now different, in other words a move event.

You can find the policies that manage this in the Publisher Event Transformation rule set. There have been a number of different shipping default configurations for Identity Manager, so the name of the rule and its exact workings have changed over time. With the advent of IDM 4 and Packages, we should see this stabilize and become more manageable.

The problem with using single valued, simple string attributes for this task is how do you handle a second AD driver in a different domain? Well if the user is only ever in a single domain, that is fine, but if you have worked with IDM then you know that one of the cool things about IDM is how easy it would be to manage users in multiple AD domains, eDirectory, and many other connected services.

If you add a second Active Directory domain to your IDM world, then the values of DirXML-ADAliasName and DirXML-ADContext will be incompatible between drivers and domains.

In the first article in this series Managing multiple Active Directory domains in one IDM system - Part 1 I discussed an approach in general of a plural version of the attributes, using Path syntax.

In the second article in this series Managing multiple Active Directory domains in one IDM system - Part 2 I discussed more of the specific implementation details of making this work. I provided some sample code for making sure you update the attributes correctly.

In the third article in this series Managing multiple Active Directory domains in one IDM system - Part 3 I worked through a good chunk of the Publisher channel rules that need to changed for the DirXML-ADContexts attribute. Still need to do the same for the DirXML-ADAliasNames attribute.

In the fourth article in this series Managing multiple Active Directory domains in one IDM system - Part 4 I worked through the rest of the Publisher channel and started in on the plural DirXML-ADAliasNames that are needed, starting with the Output transform.

In the fifth article in this series Managing multiple Active Directory domains in one IDM system - Part 5 I worked through the Input Transformation rule, and most of the Publisher channel.

As a quick recap, we will use DirXML-ADContexts and DirXML-ADAliasNames (note the plural) as multi valued, path syntax attributes instead of the single valued string equivalents that IDM ships with. In the previous article I showed some sample code on how you might update that attribute. The key is to remember to find the current value associated with this driver, remove it, then add a new value in.

Next up, is to decide WHERE do we need to make these changes? Well the easy way to manage this is to export the driver configuration as an iManager configuration file, open it in a text editor and then search for the various attribute names.

I wanted to use the latest configuration, since the IDM 3.6.1 has several possible configurations. (V4, V5, V6, and with one of the patches V7). However I happen to know that there are a number of bugs fixed in the IDM 4 Package version of the driver. I was starting a new project that needed an Active Directory driver but could not yet use IDM 4 for this project, instead had to use IDM 3.6.1. So I decided to try something funny.

I started a scratchpad Designer project, set up an IDM 4 Identity vault, added the Active Directory packaged driver, with all the options, and then I configured it for the most part.

Next I exported the driver to an iManager configuration file, and edited that, making it work with IDM 3.6.1. I have to find the time to write up that exercise as that was quite interesting to see how Packages have changed some things in the config files.

Then I searched through to find all the locations these attributes are in use. Here are my results, shown for the two different attributes, in case you want to identify them independently.

DirXML-ADContexts plural:

Input Transform:
     NOVLADDCFG-itp-SubscriberUserAdd 1 rule

Publisher Channel:
     Event Transformation:
          NOVLADDCFG-pub-etp-HandleMovesAndRenames 3 rules

     Create Policy Set:
          NOVLADDCFG-pub-cp 1 rule

     Command Transform:
          NOVLADDCFG-pub-ctp 2 rules

Subscriber Channel:
     Command Transform:
          NOVLADENTEX-sub-ctp-EntitlementsImpl 2 rules
          NOVLADDCFG-sub-ctp-UserNameMap 1 rule.

DirXML-ADAliasNames:

Filter (done)
Schema Map (done)

Subscriber Channel:
     Create Policy Set:
          NOVLADDCFG-sub-cp-Users, 1 rule
          NOVLADDCFG-sub-cp-Groups 1 rule

     Command Transform:
          NOVLADENTEX-sub-ctp-EntitlementsImpl 2 or more rules)

Publisher Channel:
     Command Transform:
          NOVLADDCFG-pub-ctp-UserNameMap 2 rules
          NOVLADDCFG-pub-ctp 2 rules

New rules needed:
     Input Transform
          itp-Convert Plural Attribute

     Output Transform
          otp-Convert Plural Attribute

On to the Publisher Command Transform for the pub-ctp rule (part of the NOVLADDCFG package) there are two rules that need to be updated named:

set cached context value on merge
remove managed attributes when object disassociated

These two were actually already discussed in part 4 of this series Managing multiple Active Directory domains in one IDM system - Part 4 in regards to the DirXML-ADContexts attribute, but as I tested more and worked through the driver further, I realized I missed the second rule (remove managed attributes when object disassociated), for both DirXML-ADContexts and DirXML-ADAliasNames.

In this case, we have a rule that is intended to handle cleaning up objects in eDirectory. This was watching for a <remove-association> event document to come through. I find this rule interesting, since I have seen many <add-association> events go through in trace, but I cannot for the life of me, ever remember seeing a <remove-association> event in trace. Of course that probably means I just did not look closely enough to have noticed it in the past.

The shipping rule is a simple Clear Destination attribute, for both the singular versions of the attributes, so by now you should be familiar with the basic approach, loop through the Destination Attribute for each of the two plural attributes, then test in each loop if the volume component has the DN of this current driver (using the automatic GCV dirxml.auto.driverdn that always returns the current drivers DN with a leading backslash and the tree name at the beginning), and if this iteration through the loop is for this driver, remove the specific value. Since it is a Structured attribute, you have to be quiet specific and specify the values for all three components, which is easy, as they are all in the current-node variable nodeset, which we get with the XPATH of: $current-node/component[@name="path"] which means, in the current-node variable, look for the node that looks like <component name="path"> and gets the string value from it. This is repeated three times, once for each of the three components of this attribute.

<do-for-each>
	<arg-node-set>
		<token-dest-attr name="DirXML-ADAliasNames">
			<arg-association>
				<token-xpath expression="./text()"/>
			</arg-association>
		</token-dest-attr>
	</arg-node-set>
	<arg-actions>
		<do-if>
			<arg-conditions>
				<and>
					<if-xpath op="true">$current-node/component[@name='volume']="~dirxml.auto.driverdn~"</if-xpath>
				</and>
			</arg-conditions>
			<arg-actions>
				<do-remove-dest-attr-value name="DirXML-ADAliasNames" when="before">
					<arg-association>
						<token-xpath expression="./text()"/>
					</arg-association>
					<arg-value type="structured">
						<arg-component name="nameSpace">
							<token-xpath expression='$current-node/component[@name="nameSpace"]'/>
						</arg-component>
						<arg-component name="volume">
							<token-xpath expression='$current-node/component[@name="volume"]'/>
						</arg-component>
						<arg-component name="path">
							<token-xpath expression='$current-node/component[@name="path"]'/>
						</arg-component>
					</arg-value>
				</do-remove-dest-attr-value>
			</arg-actions>
			<arg-actions/>
		</do-if>
	</arg-actions>
</do-for-each>

Do the same thing for the DirXML-ADContexts, and use the minor twist, that the way you find the Destination object, is to get the ./text() value via XPATH. This is the same basic approach used in the Input transform rule, NOVLADDCFG-itp-SubscriberUserAdd which operates on the <add-association> event. This is because the document you get has a single string value, the association. So by asking for the text() value of the current context (period) you get the association value. Then you can ask IDM to get you the Destination object, either by DN, which we do not know at this point, or by Association value, which that XPATH just gave us.

Of course, we could have used the Resolve token in either direction to get the DN value from the Association, but this is basically already built into the Destination Attribute token, so why bother with any extra work?

Since we are in the Publisher Command Transform, it is worth noting that the IDM 4 driver packages fix a long standing bug in the older driver configurations regarding Equivalent To Me attribute and group membership. It used to be that the rule: Prevent unassociated users from being removed from groups, came after the Set Equivalent To Me rule, and this meant that some of the users additional reciprocal attributes could get dropped at that point. Also there used to be two rules, one to handle the add case, and one to handle the remove case of group membership, when in fact a simple Clone Operational Attribute was sufficient to handle both cases. I used to just fix this every time I used the AD driver, but now with this setup, I think I will try and use this configuration for IDM 3.6.1 clients, and if I can, make a package of these Plural attribute changes for new IDM 4 clients.

Easy enough to fix, just keep the rule that does the Clone operation attribute in the older configurations and move it below the Prevent unassociated users from being removed from Groups rule, and you have it fixed. Simple error, easy to fix, pain it is not resolved in the latest configs even the V7 config that ships with the latest Active Directory driver patches. This is a bit of a shame, since Novell has decided that Configuration files are like the Pirate Code, more a series of recommendations, and not really supportable. However the good news is that with Packages in IDM 4, they will support Packages. Now as annoying as I find that attitude, I understand some of the background.

With Packages they can help you on the phone, since they can tell you toggle on Factory mode, in which case any changes you might have made in IDM by hand are ignored for the moment, run the test again, and if it works, the issue is in your changes, end of call. But if the shipping code fails, they have a reproducible test to try and work with to generate a fix.

Then as a secondary benefit, when they do realize a configuration error, issuing a new Package update to fix it is quite easy, and if you have been using Designer 4 for any length of time you will see that there have been at least 20 or more package updates since IDM 4 first shipped. At least three times now, I have restarted Designer, been asked if I was interested in checking for Package updates, which being the curious fellow that I am, I did, and new packages were found each time.

Thus if NTS debugs an issue, figures out a solution, a simple patch, that can be made available simply to everyone who wants it, without too much muss of fuss. Thus I quite understand why they are happier supporting Packages and now that they have them, would like to disavow supporting the older configurations. As a Consultant, often called in to help with existing IDM implementations and find issues. (Or called in to develop new drivers and solutions, and invariably find issues in the existing implementation along the way) I understand fully how hard it is to track down an error in driver configurations. Asking the fine folk at NTS to do it, quickly, and affordably is quite hard. Having said that, a couple of guys in NTS (Aaron, Fernando, etc) are really quite good at it, so if you can sneak it past them, they often can find your issue.

Well that wraps up the Publisher channel, now on towards the Subscriber channel, as this series starts to draw to a close. (There is a running race (5K about 3 Miles) through the Lincoln Tunnel between New Jersey and Manhattan. You literally start at the mouth of the south tube, run to the Manhattan side, see the light at the end of the tunnel (literally) take 10 steps into Manhattan day light, then turns around to run back to New Jersey. Thus I have literally seen the light at the end of the tunnel in this context).

We have three policy objects in the Subscriber channel, two in the Create policy set and one in the Command Transform.

Starting with the Create Policy set, we have the policy object sub-cp-Users which is part of the NOVLADDCFG package. Here there is a rule called "Map CN to Active Directory user logon name" which sets the destination attribute, DirXML-ADAliasName (aiming at the User Principle Name in Active Directory) and then also sets it back in the source (eDirectory).

Both these need to be modified, and in fact, because we decided to 'flatten' the DirXML-ADAliasNames in the Output transform, as we send it to the destination directory, we still need to send it with all three components. To be fair, I did not test to see what would happen if you sent it as just a simple string attribute, I wonder if the reformat operational attribute token would touch it? Easy enough to test, fire up Simulator in Designer, and lets see. As a refresher in the output transform rule (discussed in part 4 of this series) we do strip by XPATH the <component> nodes for the DN, which would not be affected by the absence of them, however, it is the reformat operational attribute that would start to get us into trouble, as it will take the value of an XPATH statement, $current-value/component[@name="path"]/text() which will be empty when the node is not there, therefore you would loose the data.

In this case it is pretty easy to 'thicken' up the attribute, since we just set the nameSpace component to the CTIME value for right now, using the Time token, the volume component with the DN value of the current driver from the GCV and the path component with the actual string value we would otherwise have sent before.

Basically we do the same thing for the Source Attribute, however, we need to first as always, check and see if there is an existing value, and if there is remove it, before adding a new one.

Code is the same basic example as used before. Nothing new to see here. The only point of interest is that this rule is only used if the UserNameMap GCV is set to edir-name-auth which means that User Principle Name is mapped to follow changes in the CN of the user. You could have been using the model where the User Principle Name is mapped to eDirectory email address or Active Directory email address, in which case this is all non-relevant. But it is nice to have the driver completely converted, instead of just for one specific use case.

Next up is the Policy object sub-cp-Groups which is part of the NOVLADDCFG package. There is a single rule here, named "Send DirXML-ADAliasName to AD when a Group is being created" which is actually interesting as I just re-read the full comments in the rule, and learned something new. The Comments in the Rule says:

"Since DirXML-ADAliasName is mapped to sAMAccountName in the smp policy, this will populate the pre-windows 2000 logon name in AD with the object name in eDirectory. Since Windows 2000 is no longer supported by Microsoft, this policy won't trim the value to 20 characters like it was required to work with that OS."

First off, it is worth mentioning that Groups in Active Directory have the DirXML-ADAliasName attribute (Singular) mapped to the sAMAccountName attribute in Active Directory. Usually, these values need to conform to the NetBIOS name convention which disallows certain characters and specifies the length of 20 characters maximum. But reading this comment I see that the subtring to 20 characters limit is removed, for the reasons given above. Interesting. Of course, we still have clients running Active Directory on Windows 2000, so that is not a great help for me, right now. I think this should have been a GCV controlled function to give the administrator the choice here.

Even having said that, I find the notion of using DirXML-ADAliasName mapped to two different things, depending on the object class to be a bit confusing and makes for a harder to understand driver. This brings up an interesting question of what to do for a Group object. Should we use the same notional plural attribute for a Group, or should we just let it map to CN and not worry about it.

The thing is, that here, we will not be having different CN values in different systems, so the need to actually make this a plural attribute for a group probably does not quite apply. I will have to think about this one, but for now, I am going to leave it alone, and not make any changes to how Groups are handled, since it does not seem like it would be necessary.

I do see a distinct lack of testing for uniqueness, however. It turns out that in Active Directory, sAMAccountName has to be unique across the entire domain, and that means between Users, Groups, and Domains. Thus this rule that escapes the source-name really ought to be double checking for uniqueness in Active Directory. If you send in a non-unique name, you get back a basically random name generated as I recall. This is probably NOT the sort of thing you would want.

This is actually a fairly painful issue when it comes to mirroring an eDirectory tree, where you very likely may have repetitive group names all over your tree. For example, every container might still have an Everyone group, if you followed the old practice from the days of Bindery! If you try and import this to Active Directory, the first one will work, the rest will be problematic.

Additionally this does not set the source value of DirXML-ADAliasName on the Group object either. I think I will try and talk to someone who works on this rule to see if they have considered these issues, and see what we can do.

Well that's about it for now. Down to one last rule left in the Subscriber channel and we can close out this series.

Viewing all 30 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>