Binding

Active Directory uses the term binding to describe the act of connecting to a directory object. This is the important first step in communicating with the Active Directory service. If you are familiar with COM or LDAP, you probably have come across the term binding before. In terms of COM, binding describes the association of a variable with an object, which can include authenticating the client. In terms of LDAP, binding describes connecting to a directory server and authenticating the client.

Getting an ADSI Object

When COM binds to an object, it can do so using a text string known as a moniker. We use monikers all the time while working at our computers. The following are examples of monikers:

 http://www.microsoft.com/adsi/ ftp://coppersoftware.com C:\books\activedirectory\reasonsfornotmakingdeadlines.doc LDAP://CN=John Doe,CN=Users,DC=coppersoftware,DC=com WinNT://coppersoftware/copper1 

Uniform Resource Locators (URLs) are one form of moniker and are used by Web browsers to load the correct component and go to the specified address. In Active Directory programming, COM uses monikers to figure out which ADSI provider to load by using the portion of the moniker up to the colon character (:). COM looks up the provider, either LDAP or WinNT for the examples above, and then passes the rest of the string along. The provider parses the portion of the string passed and returns an interface to the specified object.

COM makes it easy to use monikers by supplying a function named CoGet-Object. Given a string and the interface identifier (IID) of the requested interface, CoGetObject does all the moniker and binding work for us and returns the requested interface to the object specified in the string, if the object exists. CoGetObject is used by the GetObject function in Microsoft Visual Basic, Microsoft Visual Basic Scripting Edition (VBScript), and Microsoft JScript. ADSI provides a function similar to CoGetObject named, appropriately enough, ADsGetObject, for use in C and C++ applications. The CoGetObject and ADsGetObject functions are identical in functionality, and your applications can use either. Since ADSI and Active Directory documentation use ADsGetObject, I'll do the same in my C and C++ samples.


Object? What Object? Where?

Something that often trips up developers working with Active Directory is the vagueness of the term object. Does the object exist on the client only as a COM object, but on the server as a directory entry? Are there two objects—one on the server and one created locally? I like to think there is just one object that has various states depending on whether it is being accessed or not.

Conceptually, directory data is stored on servers as entries in a database table, which are accessed and manipulated as COM objects. Each entry is made up of attribute values that describe the object, such as common name (cn) and category (objectCategory), among others. When the object is not being accessed by any clients, it is said to be in a passive state.

When we bind to the object in the directory, we are asking to transition the object from a passive state to a loaded state. This involves making a connection to the server, retrieving the object data, and loading it locally on the client machine. Specifically, the object data is copied to an in-memory property cache that ADSI creates in the address space of the application.

As the last step in binding, ADSI examines the object's data and determines what ADSI interfaces will be supported for clients. If, for example, the object represents a person, the IADsUser interface will be supported, in addition to the basic IADs interface. Once the data is loaded locally, the object is considered to be in the running state. In this state, the client can manipulate the object's data using the appropriate methods and properties of the ADSI interfaces.

It's important to remember that in the loaded and running states, a copy of the object is loaded locally. Changes made to the local data are not automatically sent to the server. To force updating the object data on the server, the SetInfo method of the IADs interface is called by the client. This writes any changes made locally back to the server.

These various states are conceptual, but they help to illustrate how directory data is stored, accessed, and manipulated. It's important to remember that when a client manipulates an object, it's doing so on a copy of the object that is loaded locally. In Chapter 6, I'll discuss manipulating object data, the SetInfo method, and the property cache in detail.


ADsPath

The string that is used to bind to a directory object in ADSI is named the ADsPath. It is central to many ADSI operations. The ADsPath is passed to the GetObject function in Visual Basic, VBScript, and JScript. C and C++ applications use the ADsGetObject function. These functions take the ADsPath string, call CoGetObject, and return a reference to a COM object representing the directory entry.

The format for the ADsPath depends on the ADSI provider being used. Here are the formats for the ADSI providers used in this book. Items between the square brackets are optional.

 LDAP:[//hostname[:portnumber][/distinguishedname]]
GC:[//hostname[/distinguishedname]]
WinNT:[//domainname[/computername[/objectname[,classname]]]]
WinNT:[//domainname[/objectname[,classname]]]
WinNT:[//computername[,computer]]

The only required items are the provider name and a colon character. For example, if you want to access just the provider's namespace but not a specific directory object, you would use LDAP:. If anything does appear after the colon, a double-slash (//) must be included. The double-slash separates the provider and the namespace that the provider supplies.

The hostname is the name of the computer being accessed, while portnumber is the TCP/IP port number. These items are optional, but they allow ADSI to contact a specific server and port if need be. The hostname can be a NetBIOS name such as COPPER1, a fully qualified domain name such as copper1.coppersoftware.com, or a DNS address such as 209.20.250.168. The portnumber is only needed when connecting to a TCP port other than the default of 389. One scenario that requires the use of a different port occurs when a Windows 2000 domain controller with Active Directory also hosts another LDAP server, such as Microsoft Exchange Server 5.5. Generally, the other server will be configured to use a different port number to prevent both servers from attempting to fulfill requests meant for the other. The WinNT provider allows the substitution of a domain name for a specific computer name. For example, the ADsPath WinNT://COPPERSOFTWARE will connect to the Security Accounts Manager (SAM) database on the primary domain controller (PDC) for the coppersoftware domain.

The remaining portion of the ADsPath identifies the desired object and is the bulk of the string. The object path is specific to the provider. LDAP uses a particular syntax that I'll discuss in the next section, "Distinguished Name and Relative Distinguished Name." WinNT and the other providers also have their own special syntaxes.

There are too many different combinations to cover in depth at this point, but Table 4-1 shows you some real world ADsPaths for the LDAP and WinNT providers.

AdsPath Description

LDAP:

Returns a reference to the LDAP namespace object.

LDAP://CN=John Doe,CN=Users, DC=coppersoftware,DC=com

Gets the directory object named John Doe in the Users common name container in the coppersoftware.com domain.

LDAP://ldap.itd.umich.edu:389/ O=University of Michigan,C=us

Instructs the LDAP provider to contact the LDAP server at ldap.itd.umich.edu on port 389 and return a reference to the University of Michigan organization container object. The C=us portion indicates the organization is located in the United States.

WinNT:

Returns a reference to the WinNT namespace object.

WinNT://COPPERSOFTWARE

Uses the WinNT provider to return the COPPERSOFTWARE domain object.

WinNT://COPPERSOFTWARE/

Uses the WinNT provider to return an object.

Charles Oppermann, user

corresponding to the user Charles Oppermann.

WinNT://COPPERSOFTWARE/ COPPER1/Administrators,group

Uses the WinNT provider to reference the Administrators group on the COPPER1 computer in the COPPERSOFTWARE domain.

Table 4-1 ADsPath examples.

Distinguished Name and Relative Distinguished Name

The samples in Table 4-1 for the LDAP provider uses the LDAP distinguished name (DN) format to identify the desired object. The distinguished name of any directory object is the concatenation of various names separated by commas. The name that uniquely identifies the desired object within the specified container is known as the relative distinguished name (RDN). The RDN of any object is a string that contains two parts: the naming attribute (which I'll discuss in more detail in the next section) and the value of the naming attribute, for example:

 CN=John Doe 

This RDN specifies that the cn attribute, also known as the Common-Name attribute, is the attribute that names this object. The value of the cn attribute is John Doe. Put them together using an equals sign, and you have a unique string that identifies the object within its container.

You could have another user named John Doe in a different container, but because the RDN of an object is unique within its container, the entire distinguished name remains unique, as you can see from these examples:

 CN=John Doe,CN=New Employees
CN=John Doe,CN=Retired Employees

The comma serves the same role as the backslash in a file path, separating the names of the containers. The order of the names within a distinguished name starts with the child object on the left and moves right to its parent container object. This order is the opposite of the MS-DOS and UNIX file paths, in which the parent container is always listed on the left.

Naming Attributes

But what happens if two newly hired employees are both named John Doe? Well, first go out and get a lottery ticket since that's a rare event and it's your lucky day. Then simply give one user a cn like John Doe #2. The cn is just a way to name the object; other attributes hold the first name, surname, and full name of the user. A discussion of user naming conflicts appears in Chapter 9.

What's wrong with having just the string John Doe as the RDN of the object? Good question. The reason is that, just like a file system, the name of the object (or file) has to be unique within its container. You couldn't have two Readme.txt files in the same folder, nor could you have two objects named John Doe. With a file system, however, the extension is used to differentiate between types of files. So Readme.txt and Readme.doc are different and can coexist in the same file system directory. Active Directory does something similar by using the combination of the naming attribute and its value to form the RDN. For example, you could have an ou (organizational unit) named Administrators in the same container as a security group with the same name. What keeps them distinct and their RDNs unique is the attribute that's used to name them:

 OU=Administrators
CN=Administrators

The object class defines which attribute is used as the naming attribute for instances of the class. For most Active Directory classes, the naming attribute is cn, so we'll be using it a lot. Table 4-2 lists the naming attributes for some common object classes. Classes other than those listed below use cn as the naming attribute.

Naming Attribute Object Class

c

Country

dc

Domain-Component

l

Locality

o

Organization

ou

Organizational-Unit

Table 4-2 Naming attributes for Active Directory object classes.

Naming Top-Level Objects

If you're familiar with other LDAP directory servers, such as Netscape Directory Server or the LDAP server in Exchange Server 5.5, you'll notice that Active Directory uses a different convention for naming its top-level objects. The top-level object is the root container for a particular naming context, or what Active Directory refers to as a partition. The University of Michigan's LDAP directory has a top-level object with an RDN of O=University of Michigan. This means the name of the object is contained in the organization attribute of the object. The Exchange 5.5 LDAP directory also uses the organization attribute, while Active Directory does not.

Since Active Directory is tied to DNS domain structures, it uses the dc (Domain-Component) attribute as the naming attribute for objects of the domain class. Often times, you'll see an ADsPath that starts with something like this:

 LDAP://DC=coppersoftware,DC=com
LDAP://DC=microsoft,DC=com

This syntax is a way of specifying each component of the fully qualified domain name. Child domains within a domain tree would have additional parts. You wouldn't want to have just LDAP://DC=microsoft because there may be a microsoft.org, microsoft.uk, or the feared microsoft.gov. (Just kidding!)

Objects and Containers

By the way, since there is such a strong parallel between ADsPath and file paths or URLs, I want to clarify something. Everything in Active Directory is an object, including containers of other objects. In a file system, it's natural to think of folders (C:\foo\) differently from files (C:\foo\foo.txt). In directory services lingo, even containers are objects and referenced as such. So, DC=coppersoftware,DC=com is actually a reference to a domainDNS object, which also happens to be a container for various objects, many of which are containers themselves. Container objects have attributes and contain data just like any other object in the directory.

Where the relationship between objects and containers becomes interesting is with a computer class object. This object contains attributes that describe a computer: its name, description, and so on. The RDN of a computer might be CN=COPPER1 and the full DN might be similar to the following:

 CN=COPPER1,CN=Computers,DC=coppersoftware,DC=com 

Usually, a computer object isn't a container, but when a printer is attached to the computer, it becomes a container. The printQueue object associated with the printer is now contained by the computer object:

 CN=HPOfficeJet,CN=COPPER1,CN=Computers,DC=coppersoftware,DC=com 

Stringing It Together

To review what I've covered so far in this chapter; the ADsPath is the concatenation of the provider and the distinguished name of the object you're trying to access. The distinguished name itself is made up of the RDN of the object and is combined with the RDN of each of its parent containers, separated by commas. As I noted previously, the parent-child order is always right-to-left, which is opposite to the order in a URL or file path. The right-most RDN is always the parent of all other objects in the string.

Okay, now that we have an ADsPath string, what do we do with it? You pass it along...

Got Object?

I mentioned earlier that GetObject and ADsGetObject are the functions that applications use to have COM and ADSI perform their magic. GetObject is a function provided by Visual Basic and the scripting languages. Its purpose is to return an object reference to an existing COM object. It takes a string parameter—the ADsPath—that is used to determine which ADSI provider to load and what directory object to connect to.

GetObject

When you specify LDAP as the provider, COM loads the LDAP provider and hands it the rest of the string. The LDAP provider communicates with the LDAP server to retrieve the object and then returns a reference to that object. From that reference, you can call methods and access properties of the object. Listing 4-1 shows VBScript code, from the GetObject.vbs sample included on the companion CD, that uses the GetObject function. When you run this program, it contacts the server at ldap.itd.umich.edu and outputs the RDN of the University of Michigan's LDAP service.

 ` Specify the LDAP server at the University of Michigan strADsPath = "LDAP://ldap.itd.umich.edu/O=University of Michigan,C=us" ` Request a reference to the object Set objADs = GetObject(strADsPath) ` Print the name of the object
WScript.Echo "The name of the object found is: " & objADs.Name

Listing 4-1 GetObject.vbs shows how to use GetObject.

To use this sample within the Visual Basic environment, substitute Debug.Print where WScript.Echo appears. That instructs Visual Basic to print information in the Immediate window. WScript.Echo is the Windows Script object method that displays text in a dialog box or in a console window if the script is run at the command prompt.

Objects in Visual Basic

With Visual Basic, the GetObject function will return a reference to the IDispatch interface of an object. However, by using the Dim...As statement, you can request that a different interface be returned. For example:

 Dim objADs As IADs
Set objADs = GetObject("LDAP:")
Set obj = GetObject("LDAP:")

Since the variable obj is not explicitly declared, GetObject will return a reference to the IDispatch interface for the LDAP namespace object. By declaring the variable objADs using Dim objADs As IADs, the GetObject function returns a reference to the IADs interface.

When you specify the object type using Dim...As, it may be necessary to specify the object's type library. For ADSI objects, the Visual Basic project must reference the ADSI type library contained in the ActiveDS.tlb file. To reference this type library, click References on the Project menu and select Active DS Type Library as shown in Figure 4-1.

Figure 4-1 Visual Basic References dialog box showing the Active DS Type Library selected.

When Visual Basic knows about the object, based on its declaration in a Dim...As statement, it can provide the programmer with a list of the available properties and methods, as shown in Figure 4-2.

Figure 4-2 Using Dim...As to declare a specific interface in Visual Basic.

In addition to reducing the amount of typing Visual Basic programmers have to do, there is a performance improvement in declaring a variable using Dim...As with a specific interface. It allows Visual Basic to call interface functions directly. This is known as early binding.

When the object type is not known, Visual Basic must use a multistep process, asking the IDispatch interface what number (known as the DISPID) corresponds to a particular property or method and then calling the Invoke method of IDispatch and supplying the DISPID. Invoke then calls the requested method or property. This process is known as late binding, and while simple and effective, late binding is slower and might affect the performance of your application.

VBScript and JScript always use late binding.

When I write Visual Basic and VBScript code, I tend to use objADs as the name of the variable that refers to a generic ADSI object. I use names like objADsDomain, objADsUser, and so on when referring to a specific interface. I believe in naming variables according to the object being used, so if I'm looking for users who are new employees, I might name the variable that holds the object reference objADsNewEmployee. Following a convention like this helps to keep track of a number of variables that might be the same data type.

ADsGetObject

The GetObject function is available only in Visual Basic and the scripting languages. C and C++ programmers have to use the ADsGetObject function, provided by ADSI. This function takes three parameters: the ADsPath string, the interface identifier for the requested COM interface, and a pointer to a pointer variable that will hold the reference to the returned object's interface. The function returns the ubiquitous COM result code, packaged in a 32-bit value known as an HRESULT, that contains result and error codes for COM functions.

Listing 4-2 shows code from the ADsGetObject sample on the companion CD, which is the C++ version of the GetObject.vbs sample presented earlier:

 int _tmain(int /* argc */, _TCHAR /* **argv */, _TCHAR /* **envp */)
{
    IADs *pobjADs;    // Pointer to object interface
    BSTR bstrName;    // String with object name
    HRESULT hResult; // COM result code     // String to directory service
    WCHAR *pstrADsPath =
        L"LDAP://ldap.itd.umich.edu/O=University of Michigan,C=us";
    // Initalize COM
    CoInitialize ( NULL );
    // Get pointer to object's IADs interface
    hResult = ADsGetObject( pstrADsPath, IID_IADs, (void**) &pobjADs);
    // Check for success
    if ( SUCCEEDED ( hResult ) )
        {
        // Get the Name property from the object
        hResult = pobjADs->get_Name ( &bstrName );         // Check for success
        if ( SUCCEEDED ( hResult ) )
            {
            // Display Name property
            // (must use wide strings when working with COM)
            wcout << L"The name of the object found is: "
                << bstrName << endl;
            }
        else
            {
            wcerr << L"Error occured while getting the object name: "
                << hResult << endl;
            }
        // When finished with the interface, call the 
        // Release method to free object
        pobjADs->Release ();
        }
    else
        {
        wcerr << L"Error occured while getting the object: "
            << hResult << endl;
        }
    // Unload COM
    CoUninitialize ();
    // Return the result of any failures
    return hResult;
}

Listing 4-2 ADsGetObject.cpp shows how to use ADsGetObject.

With the scripting languages, GetObject always returns a reference to IDispatch, but with ADsGetObject, you can request which interface you want. For most ADSI operations, requesting the base IADs interface is sufficient. You can then use the QueryInterface method to request a different interface.

It's important to check the return value for all COM function calls. This is easily done with the SUCCEEDED or FAILED macros. Even if you're sure of the outcome, check the call through an ASSERT macro. ASSERTs will appear only in debug versions of your code and they ensure that your assumptions are validated. I've reluctantly omitted much error-checking code in the samples in this book in order to focus on the concepts being presented. However, your code isn't for educational purposes and will encounter many different failure scenarios. Don't Assume—ASSERT!



MicrosoftR WindowsR 2000 Active DirectoryT Programming
MicrosoftR WindowsR 2000 Active DirectoryT Programming
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 108

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net