Unlike SDS and SDS.AD, SDS.P has no dependencies upon the ADSI COM-based system for directory access. Instead, it supports LDAP by calling directly into the Windows LDAP library (wldap32.dll) or by using SOAP/HTTP to talk to a DSML server (see the sidebar What Is DSML? for more about DSML).
Overall Design
SDS.P is factored into a separate assembly and has no inherent dependency on System.DirectoryServices (with one minor exception that we'll discuss later). Like the LDAP API itself, SDS.P uses a persistent connection to the server as the primary metaphor for working with the directory, instead of the object metaphor favored by SDS and ADSI. A DirectoryConnection connection is established (either as LdapConnection or DsmlSoapHttpConnection), and then operations such as search, add, modify, and delete are performed on the connection using a request/ response message-passing pattern. As you may recall from our brief introduction to the LDAP API in Chapter 1, this is how LDAP works.
One of the triumphs of SDS.P is its use of a provider model to abstract the underlying transport mechanism from the operations being performed. Once we have selected a transport (either direct LDAP over TCP/IP or the DSML protocol using SOAP/HTTP), the operations performed are largely identical. We could presumably write an application that allows the user to select her transport at runtime with little difficulty! Of course, we must observe some subtle differences to make this work in practice, but it is certainly possible.
Because SDS.P is essentially a complete wrapper around LDAP, it provides a number of features to developers that are missing in SDS. One of the things we will notice immediately is that SDS.P provides complete support for asynchronous invocation using the standard .NET asynchronous design pattern (Begin/End semantics, delegates, IAsyncResult, etc.). This makes it much easier to use the LDAP API's native support for asynchronous messaging, which might be very important in high-performance, multithreaded server applications.
Another important distinction between SDS.P and the ADSI model is that just like the LDAP API, SDS.P does not attempt to convert the raw data in the store to a "stronger" type. Programmers used to ADSI may be shocked to discover that LDAP actually stores integers, Booleans, and dates as strings! In the LDAP world, everything is interpreted as either a string or binary data. If we want to reinterpret the data as a type that is more convenient for processing in a strongly typed language such as .NET, then we have to perform a lot of extra work. The upside of this is that we eliminate the overhead that ADSI imposes with its mapping, along with any possible errors related to ADSI not being able to do the mapping in the first place. Data-mapping errors often surface with non-Microsoft directories, especially those that don't support LDAP version 3.
Finally, several other LDAP features are available in SDS.P that simply are not available in SDS. Some of these include access to the full set of secure binding mechanisms supported by LDAP, fast concurrent binding in Windows 2003 Active Directory and ADAM, and full client and server certificate processing for SSL/LDAP connections.
How Is it Organized?
One of the first things we will notice when looking at the SDS.P namespace is that nearly 100 types are defined there. It seems like a lot compared to SDS, but once you get the hang of it, it is straightforward. The namespace can be loosely grouped into the following categories:
Connection Types
As we just mentioned, the primary metaphor for SDS.P is the connection to the server, so this is where we start.
DirectoryConnection
DirectoryConnection defines the abstract base class for the other connection classes.
LdapConnection and DsmlSoapHttpConnection
These are the actual classes we instantiate to establish a connection to the directory using our protocol of choice. Note that DsmlSoapHttpConnection derives from another abstract base class, DsmlSoapConnection, in order to provide session state support. Presumably, this class could also be used as a base for adding DSML support on other transports, such as what is promised by technologies like Windows Communication Framework (WCF, previously called Indigo).
DirectoryIdentifier, LdapDirectoryIdentifier, and DsmlDirectoryIdentifier
These classes are used to provide different mechanisms for identifying a directory server. LDAP typically uses DNS names and DSML typically uses URIs, but Microsoft's LDAP API also supports serverless binding to Active Directory using the locator service. We'll discuss serverless binding in more detail in Chapter 3, in the context of SDS.
A variety of other classes and enumerations also are used to support various authentication mechanisms and server options used by LDAP, including LdapSessionOptions and its various options, as well as AuthTypes, which defines which type of secure authentication to use. Detailed control over session options is one of the most distinguishing features that sets SDS.P apart from SDS. For example, SDS.P offers very fine-grained control over the behavior of referrals, the version of LDAP to use, and SSL options, to name just a few.
LDAP Operation Types
These classes define how various operations are performed on the directory, such as searches, additions, deletions, and modifications. All of these come in matching request/response pairs to be used on the DirectoryConnection.SendRequest method and its peers. These classes form the key abstraction over the connection classes, as they are used interchangeably over LDAP and DSML.
The DirectoryRequest and DirectoryResponse classes are the abstract base classes for all of the other message pairs. The actual concrete classes for the directory operations are implemented in the following classes:
The SearchRequest and SearchResponse pair comprises the most commonly used operation. Searching has a variety of options and other classes associated with it for accessing the results of searches, including partial result sets. These classes include SearchResultEntry, SearchResultEntryCollection, SearchResultAttributeCollection, SearchResultReference, and SearchResultReferenceCollection. A number of LDAP controls also are used to modify the behavior of searches.
LDAP Data Types
Data in LDAP revolves around attributes and their values. The DirectoryAttribute class is the core class for representing this in SDS.P. As we discussed earlier, LDAP essentially stores all data as strings. Data can be interpreted as either a string or binary data (a byte array).
Even though LDAP attributes support specific syntaxes such as strings, Booleans, numeric data, and dates, the actual storage is a string representation of the data. Thus, a Boolean is actually stored as the string trUE or FALSE and an integer value of 1234 is stored as the string "1234". The directory service enforces the syntax rules for the attribute when it is saved, but does not convert to any kind of platform-native binary data type.
This is probably the aspect of SDS.P that is the most different from the ADSI-based providers. ADSI goes through great effort to read a directory's schema and devise helpful mapping of LDAP data to standard COM variant data types such as VTI4 (variant holding a 4-byte integer) and VTBool (variant holding a Boolean). SDS.P gives us the raw data and leaves any sort of syntactic mapping to the devices of the developer.
As a result, the primary methods on DirectoryAttribute for getting the data will return either a string or a byte array. Under the hood, DirectoryAttribute attempts to convert the binary data into a UTF8 string. If that succeeds, it will return a string by default. If it fails, it will return a byte array. We can also request the data as either strings or byte arrays by calling the GetValues method.
A few other classes support DirectoryAttribute, such as DirectoryAttributeCollection, DirectoryAttributeModification, and SearchResultAttributeCollection. We may use them in some examples later on, but we won't cover them in more detail.
LDAP Control Types
LDAP controls are used to modify the behavior of an LDAP operation on the server. They provide an extensibility mechanism for the server to support operations that are more advanced. LDAP controls may be generally supported by the LDAP specification or may be proprietary to the specific directory implementation. SDS.P ships with many of the supported controls for Active Directory and ADAM.
Finally, SDS.P has a base class for all of these controls, called DirectoryControl. Unlike many of the base classes used in SDS.P, this one is not abstract, which means we can instantiate it directly. This gives developers the ability to use controls that may be supported on their directory server, of which SDS.P has no strongly typed representation. The developer simply needs to know the control's Object Identifier (OID), whether it is server-side or client-side, and what the binary input data for the control must look like. Developers may also use this base class to create their own strongly typed controls.
Part I: Fundamentals
Introduction to LDAP and Active Directory
Introduction to .NET Directory Services Programming
Binding and CRUD Operations with DirectoryEntry
Searching with the DirectorySearcher
Advanced LDAP Searches
Reading and Writing LDAP Attributes
Active Directory and ADAM Schema
Security in Directory Services Programming
Introduction to the ActiveDirectory Namespace
Part II: Practical Applications
User Management
Group Management
Authentication
Part III: Appendixes
Appendix A. Three Approaches to COM Interop with ADSI
Appendix B. LDAP Tools for Programmers
Appendix C. Troubleshooting and Help
Index