Directory-Enable Apps with ADSIThe Active Directory Service Interfaces Set Allows You to Use a Key Feature of NT5 Todayby Dan Fox Reprinted with permission from Visual Basic Programmer's Journal, Feb 1999, Volume 9, Issue 2, Copyright 1999, Fawcette Technical Publications, Palo Alto, CA, USA. To subscribe, call 1-800-848-5523, 650-833-7100, visit www.vbpj.com, or visit The Development Exchange at www.devx.com. When Windows 2000formerly NT 5.0arrives, developers will have better access to the directory services information used and stored by NT through the much-talked-about Active Directory. This single source of information allows developers to more easily directory-enable their applications using directory data such as security, computer, printer, file, and user information.
Developers often face the problem of how to retrieve and authenticate users in a networked environment. As companies become more distributed, the number of directories that store user information is multiplying. I know of one company that stores user information in the Novell Directory Services (NDS) for file and print services, Windows NT Directory Services (NTDS) for application services, Microsoft Exchange for LAN-based e-mail, and a directory for Internet access. This plethora of directories leads to administrative confusion and makes developers wary of integrating their applications with only one directory, as their companies change from one network OS to another. Microsoft has addressed this problem by developing ADSI, a core set of COM interfaces designed to abstract the capabilities of any particular directory service and provide a consistent interface for developers. This concept parallels the effort by Microsoft to abstract the capabilities of data providers through the OLE DB interfaces. And, just as ActiveX Data Objects (ADO) allows VB developers to use OLE DB interfaces, the ADSI objects provide an automation interface allowing VB, VBA, and VBScript developers to use the ADSI interfaces. At the highest level, ADSI defines namespaces through which you obtain access to a hierarchical directory service. Each namespace corresponds to an ADSI provider that acts as the interface into a directory service in the same way an OLE DB provider interfaces with a database. The current downloadable versionADSI 2.0includes ADSI providers for Microsoft NTDS, Microsoft Internet Information Server (IIS), LDAP, Novell NDS, and the Novell 3.x bindery (see Figure 1 and the Resources sidebar). After setting a reference to the ActiveDS type library in your project, you can generate a list of the namespaces accessed through the providers by querying the IADsNamespaces object using the GetObject function, passing it the string "ADS:":
Once you obtain a reference to the namespace object within the namespaces container, you can use the Name, ADsPath, and Class properties to print out the basic information about the namespace. The Name and Class properties identify the name and type of object that was returned, such as "WinNT:" and "Namespace", respectively. However, the ADsPath property is more interesting because it specifies the fully qualified name of the object within the directory hierarchy. In fact, using ADsPath, you can get a reference to any object in the directory using GetObject. To get a reference to the Windows NT domain MyDomain, call GetObject with the ADsPath of the domain:
Retrieving an object using the generic IADs interface does not preclude the object from also being a container. In the last example, an NT domain is also a container for computers that have joined the domain. To enumerate those computers, point the instance of objDomain to an instance of an IADsContainer and enumerate the computers:
You're probably wondering how to manipulate these objects, not simply print out the directory structure. ADSI not only provides generic objects to query the directory structure, but it also implements a standard set of directory objects that include the most common objects found in a directory tree. Providers can then implement and augment these standard objects (see Table 1). You can use these directory objects to access objects and perform actions within the directory (see Figure 2). Using the more specific objects, you can now view particular properties of the object instead of only its ADsPath and Name:
You should be aware that not all ADSI providers implement all the available properties and that the properties themselves might be mandatory or optional. For example, the Windows NT 4.0 provider does not implement the MemorySize and StorageCapacity properties of the IADsComputer object, among others. You can find the complete list of properties and methods supported by the NT 4.0 provider in the online help (see the Resources sidebar). You can also query the provider for what it supports using the Schema object and the standard object IADsClass:
The other benefit of exposing the directory objects: They might also contain methods that allow you to modify the directory. For example, you might create a new user and add the user to an NT group (see Listing 1). In addition to these built-in objects, ADSI providers can extend the set of objects, methods, and properties or even allow administrators to augment the directory. This is an important feature of NT5 because it enables administrators to store all types of information in the Active Directory, all of it accessible through ADSI.
To provide an example of how you might use ADSI, I built a three-tier application that returns orders from a SQL Server database based on the region of the user, as determined by the user's NT group membership. First, the user interface tier of the application determines the user's NT credentials. Next, the business services tierimplemented as an ActiveX componentuses ADSI to determine which group and region the user is a member of. The component then passes the group information to SQL Serverthe data services tierwhich runs a stored procedure to return rows from the user's region (see Figure 3). This basic problem might have other solutions, but this ADSI implementation has the benefit of being directory-independent. It also performs row-level security while not requiring the additional SQL Server administration to implement views or use SQL Server's security. The core of this solution is the class module that implements the component (see Listing 2). For this class, I created two public propertiesUserAccount and UserDomainthat the GetUserGroup private function uses to query ADSI and determine which group the user is a member of. The GetUserGroup function uses a local array to store the possible regions and their IDs. It tests against four regions that exist as NT groups and that you must create using the User Manager utility on the NT Server. After obtaining a reference to the group through the IADsGroup interface, the function uses the IsMember method to determine which group the user resides in:
If the method finds that the user is a member of one of the groups, the function exits without checking the others, because the user cannot be a member of more than one region. The ListOrders method calls GetUserGroup and uses ADO 2.0 to call the stored procedure GetOrders, passing the region ID to the procedure (see Listing 3).
Dan Fox lives in Overland Park, Kan., and is a consultant and trainer for Solutech Inc. He is a Microsoft Certified Systems Engineer, Solution Developer, and Trainer who develops client/server and intranet solutions and teaches Visual Basic, SQL Server, and Visual InterDev. Reach Dan by e-mail at dfox@solutechinc.com. |