Working with the DirectoryContext Class
At the center of SDS.AD is a class called DirectoryContext. Directory-Context gathers the information needed to specify which directory to connect to and what credentials to use for the connection, in addition to the type of object that we wish to create. As we glance through the rest of the SDS.AD classes, we notice that nearly all of them seem to take a DirectoryContext object as an argument for their methods and constructors.
General Usage
To learn more about how DirectoryContext works, let's first look in Listing 9.1 at the class in terms of its public constructors and properties and its key supporting enumeration, DirectoryContextType.
Listing 9.1. The DirectoryContext and DirectoryContextType Public Members
public class DirectoryContext { // Constructors public DirectoryContext(DirectoryContextType cType); public DirectoryContext( DirectoryContextType cType, string name); public DirectoryContext( DirectoryContextType cType, string username, string password); public DirectoryContext( DirectoryContextType cType, string name, string username, string password); // Properties public DirectoryContextType ContextType { get; } public string Name { get; } public string UserName { get; } } public enum DirectoryContextType { Domain, Forest, DirectoryServer, ConfigurationSet, ApplicationPartition } |
We see right away that each constructor requires us to specify a DirectoryContextType enumeration value. This value helps SDS.AD decide how to use the other parameters we have specified to build the object that uses DirectoryContext for initialization.
The first constructor requires only a single DirectoryContextType parameter. It is intended for creating only Domain and Forest objects, as the other types of objects, which correspond to the DirectoryServer, ConfigurationSet, and ApplicationPartition enumeration values, require the name parameter to be set in order to provide enough information to build the object.
The other three constructors also allow us to provide a name, a username and password combination, or all three additional parameters.
The name parameter is used for determining to which directory to connect. This might be the DNS domain name of a domain for an Active Directory domain, or it might be the DNS domain name of a specific ADAM server with its LDAP port included. It depends, again, on what type of context we are creating.
The username and password parameters are used for supplying alternate security credentials for the connection. This works in much the same way as with the username and password parameters on the DirectoryEntry class that we described in Chapters 3 and 8. If we do not use one of the constructors that take a username and password parameter, then the current security context is used to make the connection. This is also just like the behavior we already described with the DirectoryEntry class. As usual, we generally recommend avoiding the use of specific credentials, but we covered the scenarios where we consider that valid in Chapter 8, and we will not rehash it here.
As we might guess, the Domain and Forest enumeration values are relevant only when working with Active Directory. After all, ADAM does not have domains and forests! The ConfigurationSet value is relevant only when working with ADAM, as configuration sets are exclusively an ADAM concept. On the other hand, DirectoryServer and ApplicationPartition are relevant for both directories.
Note: DirectoryContext Cannot Be Used with ADAM Users
For whatever reason, Microsoft designed DirectoryContext to work exclusively with Windows users. Currently we cannot supply the credentials of an ADAM user, and use them to create any kind of SDS.AD class that references ADAM. Perhaps Microsoft did this because most management of ADAM instances is still done with Windows users. Perhaps it was difficult to make some of the APIs work together, or perhaps Microsoft was concerned about the potential security issues around simple LDAP binds that we described in Chapter 8. In any event, make sure you always use a Windows user when specifying explicit credentials.
The upside of this limitation is that when SDS.AD uses LDAP, it always does a secure LDAP bind (with Signing and Sealing as well). We never have to worry about SDS.AD passing our credentials on the network in plaintext.
Examples of Using DirectoryContext
Perhaps the easiest way to get the hang of the DirectoryContext class is to use it in several examples to build specific types of other SDS.AD objects. In each example, we show what we are trying to do and what our various assumptions and constraints are for doing it.
In Listing 9.2, we show three approaches for building a Forest object. The first uses a DirectoryContext with only the DirectoryContextType supplied. This assumes that we want to use our current security context to build a Forest object for the current user's Active Directory forest. The second uses a shortcut on the Forest class to do the exact same thing, without actually creating an explicit DirectoryContext object. The third uses both a forest name in the name parameter and alternate credentials to access an Active Directory forest that our current security context might not trust.
Note: Watch Out for IDisposable Again in SDS.AD
As with our core classes in SDS, many of the classes in SDS.AD implement IDisposable as well. While we are not aware of any bugs related to object cleanup in SDS.AD, as there are in SDS in .NET Framework version 1.x, we still recommend proper cleanup of these objects using the standard patterns we showed in Chapter 3.
Listing 9.2. Using DirectoryContexts to Create Forest Objects
//using System.DirectoryServices.ActiveDirectory; Forest currentForest = null; Forest alternateCurrentForest = null; Forest otherForest = null; //create a new DirectoryContext for a Forest //using defaults for name and credentials DirectoryContext context = new DirectoryContext( DirectoryContextType.Forest); using (currentForest = Forest.GetForest(context)) { Console.WriteLine(currentForest.Name); } //Use the shortcut static method on the Forest class to //do the same thing as above using (alternateCurrentForest = Forest.GetCurrentForest()) { Console.WriteLine(alternateCurrentForest.Name); } //Now, connect to a completely different forest //specifying its name and the credentials we need to //access it DirectoryContext otherContext = new DirectoryContext( DirectoryContextType.Forest, "other.yourforest.com", @"othersomeone", "MySecret!0"); using (otherForest = Forest.GetForest(otherContext)) { Console.WriteLine(otherForest.Name); } //OUT: //main.myforest.com //main.myforest.com //other.yourforest.com |
We can use very similar logic to create a Domain object, so we will not bother to show that. Essentially, we just need to supply the name of the domain we wish to access and specify the DirectoryContextType.Domain enumeration value where appropriate. Note, however, that we might also access other domains in the same forest this way instead of completely unrelated domains outside of our forest. Depending on what we are doing, alternate credentials may or may not be required.
Now, let's access a specific server. Listing 9.3 shows an example of accessing an Active Directory domain controller by its DNS name and then an example of accessing an ADAM instance on our local machine using the local administrator account.
Listing 9.3. Using DirectoryContext to Access Specific Servers
//using System.DirectoryServices.ActiveDirectory; DomainController dc = null; AdamInstance adam = null; //create a new DirectoryContext for a //domain controller using a name and //default credentials DirectoryContext dcContext = new DirectoryContext( DirectoryContextType.DirectoryServer, "mydc.mydomain.com"); using (dc = DomainController.GetDomainController(dcContext)) { Console.WriteLine(dc.Name); } //Now, connect to a local ADAM instance //specifying a port and local admin credentials DirectoryContext adamContext = new DirectoryContext( DirectoryContextType.DirectoryServer, "127.0.0.1:50000", @"MYMACHINEadministrator", "MySecret!0"); using (adam = AdamInstance.GetAdamInstance(adamContext)) { Console.WriteLine(adam.Name); } //OUT: //mydc.mydomain.com //myadaminstance.mydomain.com |
We can use the DirectoryServer enumeration value to build both DomainController and AdamInstance objects, as they both derive from the same base class, DirectoryServer. In each case, we have the option of specifying credentials and may specify the server name in various formats, including DNS domain names and IP addresses. We may also include the port to use if needed, as we might with ADAM. The rules governing the available syntaxes are the same as what we spelled out in detail in Chapter 3 for specifying servers and usernames. Given that the same set of technologies is used under the hood, this should come as no surprise.
If we have an ADAM configuration set, we may use similar logic to the AdamInstance part of Listing 9.3 to create a ConfigurationSet object by changing our DirectoryContext to use the ConfigurationSet enumeration value and changing our other classes to use the appropriate ConfigurationSet class. Note that we cannot use our normal using statement here to take care of object cleanup because for some reason, ConfigurationSet does not support IDisposable, even though it has a Dispose method! We assume that this is just a small glitch that will be cleaned up in a future service pack. Given that this is all new functionality, there's bound to be a rough edge here or there.
Unfortunately, we cannot show every possible example of using a DirectoryContext object to build every imaginable type of SDS.AD object, but we hope that by now we've established the ground rules that we can use for building the other types. The key is to understand which values of the DirectoryContextType enumeration we can combine with values of the name parameter (potentially including a null value) to achieve our goal. We also have most of the same flexibility with the credentials that we have with SDS.