Query Active Directory from the iSeries
June 15, 2005 Michael Sansoterra
The code for this article is available for download.
With respect to iSeries programs, real time integration with other platforms and databases has been one of my favorite pursuits. Unfortunately, as a consultant, I perceive that many have left the iSeries because of the perception that certain things could not be done programmatically. Of course, with the addition of Java and the APIs available to the ILE programmer, we know that the iSeries can do about anything–sometimes you just have to dig a little to find out how. In this article, I’m going to discuss a technique to query Active Directory using LDAP and Java.
What is Active Directory?
Microsoft‘s Active Directory (AD) stores information about objects on a Windows 2000 Server or Windows Server 2003 network including users, groups, computers, printers, SQL Server data bases, and so forth, as well as the attributes related to those objects. For instance, Active Directory contains boatloads of user information, including e-mail addresses, department, addresses, manager, group member ship and the like (see Figure 1 below and the immense number of tabs available to fill in information related to a Windows user.) If your Windows administrator maintains any of this information and if your iSeries app needs it, you can get it!
For a comprehensive introduction to AD, go to Microsoft’s Active Directory site.
Practical Uses
So why would we ever want to retrieve information from AD? Many of our iSeries customers use Exchange as their e-mail server. The address book for Exchange server is stored in Active Directory. (And most administrators don’t take the pains of duplicating e-mail addresses a second time on the iSeries!) My primary purpose for researching this topic was the desire to retrieve e-mail addresses from the Exchange server’s address book in real time. If I want an up-to-date list of organizational users that have the name Michael in their e-mail address, it’s just as easy as requesting AD to “give me all e-mail addresses containing the name Michael.” Bear in mind that this does not apply to contacts stored in a local address book stored on a PC somewhere.
Figure 1: Active Directory contains boatloads of information about users and systems.
Another application I can see for querying Active Directory in large organizations is to take advantage of group membership information. If group membership as dictated by corporate policy is already defined in Windows, why duplicate it on the iSeries? For example, assuming user names are consistent between Windows and the iSeries, an application could query AD to determine if a certain user belongs to a group that can perform a certain function. Of course, this would only apply to application level security, not to object level security.
I’m relatively new to the AD world so I imagine there are probably many more uses for this.
What Is LDAP?
LDAP, the Lightweight Directory Access Protocol, is a widely established standard for providing access to directory services. Most platforms, including the iSeries, have an LDAP server. LDAP acts as a standardized bridge or interface to a directory service. So that it can be accessed universally (including access by non-Windows clients), Microsoft allows Active Directory to be accessed via LDAP.
AD and directory services in general are optimized for read operations, but LDAP does provide read/write capability to AD. This article will only cover how to read from Active Directory, not how to update it, although this is possible.
For a good overview of LDAP, click go to www.peterindia.net/LDAPOverview.html.
Active Directory Organization
AD stores information hierarchically similar to the way folders are organized on a Windows PC. Just as a C: drive has a root and subfolders, Active Directory has a root and subcontainers holding information related to various objects. To query an LDAP server, you need to specify a base (i.e. starting point for your query) and the characteristics of the object you’re trying to retrieve.
For example, when searching for specific files on your PC, you normally commence a search with a specific subfolder (because searching from the root would take longer and waste resources) and you often specify a wildcard and perhaps a document type (*.doc, *.xls, and so forth) Similarly, when querying AD you’ll usually start with a sub-container in the directory’s hierarchy and specify a particular object type (computer, person, etc.) and perhaps a wildcard pattern to narrow the search.
Figure 2: A sample screen of an AD organization for a fictional domain called Contoso.com. As you can see the structure in AD is similar to file system storage with folders (or containers) housing a variety of objects in the directory and can even have subfolders.
AD contains several pre-defined self-describing containers (Users, Computers, etc.) but that doesn’t mean that these containers actually house objects. User-defined containers called organizational units can be created for organizing and storing objects. For example, the Users container doesn’t necessarily have to store your network’s user IDs. They are placed here by default, but user objects can be moved or created by an administrator in user-defined organizational units. In the sample domain shown in Figure 2, some users are stored in the employees container. (For those who may be wondering about the name Contoso.com, yes, I was doing some Microsoft training when I created this.)
By comparison, think of the My Documents and My Pictures folders being used as the default storage area for files on a Windows PC. However, most advanced users create their own folder structure for storing and organizing documents. The same holds true with Active Directory. If you plan to query AD, get with your network administrator to make sure that you understand your company’s AD structure and the types of objects contained therein. In order to create a query, you’ll need to understand how to specify a base for the search and how to specify filter criteria.
Finally, understand that AD stores information related to an object (such as a user or computer) as attributes of the object. When you query AD, most of the useful information will be pulled from the object’s attributes and each object type has its own set of attributes. For example a person object has an e-mail address attribute, a computer object has an operating system version attribute and a printer will has a color capable attribute.
AD Query Syntax
To query AD using LDAP, you must specify two things:
- A base context or starting point for your query
- A search filter
The base context is comprised of the domain name followed by the organizational unit or container you want to start with.
For example, if your domain name is mycompany.net and you want to start a search in the users container, your query base would be specified using the following LDAP syntax:
CN=users,DC=mycompany,DC=net
As you can see, this base consists of naming attributes followed by the actual object in AD. Here are the naming attributes depending on which object you need to reference:
AD Object |
Naming |
Domain |
DC |
Organizational |
OU |
Container |
CN |
For another example, if you want to submit a query that starts in the domain controllers organizational unit (OU), your base would look like this:
OU=domain controllers,DC=mycompany,DC=net
Once you know where to start your search, you create a filter. Here are the operators you’ll need to use:
& - And | - Or ! - Not > - greater than < - less than = - equal (similar to Like if the criteria includes a wildcard: *)
Here’s an example of a filter that finds all objects with a class of person that have a name attribute beginning with P:
(&(objectClass=person)(anr=p*))
The one strange thing to beware is that the conjunctions (AND,OR,etc.) are prefixed before the set of criteria instead of being placed in between them. Also the asterisk is used for pattern matching as shown in the above example.
Here’s an example of a filter that finds all printers that are color capable on Server01:
(&(&(uncName=*Server01*)(objectCategory=printQueue)(printColor=TRUE)))
In the filter, the criteria are based on the attributes of the specified object. So how does one know the specific attributes that AD provides with any given object class (i.e. that the printer class has an attribute called printColor)? The answer is to look on Microsoft’s MSDN site for the Active Directory schema. As you can see from the list, there are quite a few classes available in Active Directory and each class has its own large set of attributes. Some common AD object classes are: Computer, Contact, Group, PrintQueue, Person and OrganizationalUnit.
The Java Program
ADQuery.java code that you can get at the top of this story provides an example of how to query AD. The Java Naming and Directory Interface (JNDI) API is used to perform the query using the LDAP service provider. JNDI’s purpose is to create a generic programming interface for working with any number of existing and future directory service providers such as LDAP.
JNDI is to directory services as JDBC is to databases. Both APIs are designed to work with vendor pluggable “providers” or “drivers” to communicate with the back end service. Fortunately, the LDAP service provider is provided by Sun Microsystems. For a good introduction to JNDI, see the tutorial on Sun’s Web site.
The sample Java program does the following:
- Creates a hash table for storing the various connection attributes required to submit an LDAP query to an Active Directory Domain Controller (directory service provider, URL, security credentials, etc.) Of course, you’ll need to replace some of the info such as domain, user, etc. with your organization’s info.
- A query root and a query filter are constructed to find all of the people in AD beginning with the letter P. (A sample query for finding a printer is given as well.) Likewise, a list of attributes to return for each object is specified. If an attribute is not found for a given object, a null is returned. For instance, if you query printer objects and request the e-mail attribute you will get a null because AD doesn’t hold an e-mail attribute for printers.
- An LDAP context is established using the data stored in the hash table in step 1. The scope of the query is set to SUBTREE so that the query traverses all of the underlying objects. The root and filter created in step 2 are passed to the LDAP context object which then passes the request to AD.
- The query is submitted using the search method and a javax.naming.NamingEnumeration object is used to collect the output of the query, which includes object names and the requested attributes. A loop is used to display the output to the console.
As you can see, with the help of JNDI the process of querying AD is relatively easy.
Limitations and Gotchas
AD is optimized for speed and by default will return a maximum of 1000 rows, so if you need more data than this you’ll need to segment your data by issuing multiple queries with appropriately tailored selection criteria or use a technique called paging. To get more than 1000 rows you need to change your domain controller’s group policy as outlined on Microsoft’s Web site.
Make sure the user you use to query AD has the proper authority to query specific containers and organizational units.
Make sure you understand the hierarchy of Active Directory and in particular your company’s policies for storing objects. For example, you may store Contact objects and Person objects in different containers or OUs in AD. If this is the case and you’re looking in AD for all e-mail addresses including contacts, you may need to submit multiple queries–one for each area in AD.
Some multi-value attributes in AD, such as group membership lists, may need to be converted to an array to be useful.
When getting started (using Windows Server 2003), the GUI querying tool found in the “saved queries” tree of the Active Directory Users and Computers console along with the command line tools DSGET and DSQUERY were very helpful with helping me locating objects and learning how to create queries.
For more help on this topic, consult the Internet of course! Specifically for the Java programming, I suggest looking on Sun’s website, including the aforementioned JNDI tutorial and the JNDI forums (which is primarily where I learned how to create these queries using Java).
For help with writing queries for Active Directory (finding specific attributes, etc.), do not limit your internet search to be Java specific. There are plenty of .NET related articles that can help your understanding of how to query AD. (Besides–the base language of C# and Java are very similar.)
Integrating with Legacy Applications
Because this sample program writes query results to the console (which is not much value to an application) it will need to be modified to meet your needs. Unless your apps are Java based, you’ll probably need to integrate this Java code with RPG or C. Although beyond the scope of this article, using the Java Native Interface to instantiate Java objects and invoke Java methods is one possibility. My favored method, though, is to create an SQL User-Defined Table Function wrapper around this logic. By creating a table function to return attributes of AD objects, integrating with a host language becomes as easy as using embedded SQL.
Here is a hypothetical table function called ADEMail that could be embedded inside an RPG program to retrieve all e-mail addresses based on a given pattern. In this case, the Java code underlying the table function would be specifically written for retrieving e-mail addresses.
C/Exec SQL C+ Declare EMAddresses Cursor For C+ Select Email C+ From Table(ADEMail(:eMailPattern)) As EM C/End-Exec
For info on calling Java code from a host language, see the JNI topic in the IBM Information Center.
For info on creating Java SQL table functions, see the Java SQL routines section of the IBM Developer Kit for Java.
The Integration Factor
The ability to query Active Directory can reduce various network administration tasks (such as storing e-mail addresses in multiple places) and give your application the ability to have an awareness of network objects in your environment. This kind of tight integration can make platform barriers in your organization disappear.
Michael Sansoterra is a programmer/analyst for i3 Business Solutions, an IT services firm based in Grand Rapids, Michigan. Click here to contact Michael Sansoterra by e-mail.