ADSI Support in .NET


To support programming against ADSI in managed code, the .NET Framework provides the System.DirectoryServices namespace. Rather than forcing you to write legacy code to COM interfaces, which would degrade performance, the System.DirectoryServices namespace is implemented as all managed code, although COM versions of ADSI still work inside of .NET. If you need to maintain legacy applications, you should still write to the COM interfaces. However, for any new development that you want to perform using .NET, you should leverage the System.DirectoryServices namespace.

The .NET Framework SDK includes the complete documentation for this namespace. I'll cover just a subset of the functionality of this namespace next . We will look at three tasks that you will commonly perform with this namespace against Active Directory: searching, reading entries, and writing entries. Please note that the DirectoryServices namespace works with more than just the LDAP provider and Active Directory. You can also use it with IIS, NDS, and WinNT providers.

Searching Active Directory Using the DirectoryServices Namespace

You will primarily use two classes in the DirectoryServices namespace: DirectoryEntry and DirectorySearcher . The DirectoryEntry class is for accessing a node in the directory; we'll discuss it when we look at reading and writing entries in the directory. The DirectorySearcher class is used to search the directory. The result it returns is a .NET collection, which you can iterate through.

The first thing you do with the DirectorySearcher class is to construct a new object of that class. The constructor for the class takes an optional argument ”the search base where the search will start. If you do not specify the search base, the search base will default to the domain. You can specify the search base at a later point using the SearchRoot property.

Next, you specify a search scope. The search scope can be Base (this level only), OneLevel ( first-level children), or Subtree (entire subtree below the base). You set the search scope using the SearchScope property on the instance of your DirectorySearch class.

Next you specify the LDAP filter you want to use to search the directory. Here are some examples of filters:

  • (objectClass=*)     All objects

  • (sn=n*)     All objects where the surname starts with n

  • (&(objectCategory=Person)(objectClass= user ))     All users

  • (&(objectCategory=Person)(mail=*))     All users with e-mail addresses

  • ((department=100)(department=200))     All objects in department 100 or 200

The next step is to specify the properties to load by using the PropertiesToLoad collection's Add method. If you do not specify the properties to load, ADSI will load all properties for the returned objects.

You can optionally specify the page size, size limit, server time limit, and other properties. The page size is useful if you believe that a large number of results will be returned. Instead of dumping all the results back, ADSI will return pages of results based on the size you specify.

The final step before getting your results is to perform the search by calling the FindAll method. This method performs the search and returns a collection of all objects that meet your criteria. The key thing about the results that are returned is that they are read-only. If you want to modify a result, you should request its associated DirectoryEntry object by using the GetDirectoryEntry method on the returned result object. You can access any of the properties that are returned in your results using the notation object.Properties("propertyname").Item(0) .

The following code, from the WinForms sample that's included with the companion files, shows how to use the DirectorySearch class:

 On Error Resume Next If txtLDAPPath.Text <> "" Then     Dim oRoot As New _         System.DirectoryServices.DirectoryEntry(txtLDAPPath.Text)     Dim strAlias As String     strAlias = txtAlias.Text          Dim oSearcher As New _         System.DirectoryServices.DirectorySearcher(oRoot)     'Can change search base using SearchRoot     'oSearcher.SearchRoot = "NewSearchBase"     'Set the search to subtree to search entire tree     oSearcher.SearchScope = DirectoryServices.SearchScope.Subtree     oSearcher.Filter = "(mailNickname=" + strAlias + ")"     oSearcher.PropertiesToLoad.Add("cn")     oSearcher.PropertiesToLoad.Add("title")     oSearcher.PropertiesToLoad.Add("department")     oSearcher.PropertiesToLoad.Add("physicalDeliveryOfficeName")     oSearcher.PropertiesToLoad.Add("telephoneNumber")          Dim oResults As System.DirectoryServices.SearchResultCollection     oResults = oSearcher.FindAll()          Dim oResult As System.DirectoryServices.SearchResult     For Each oResult In oResults         MsgBox(oResult.Path)         MsgBox(oResult.Properties("cn").Item(0))         MsgBox(oResult.Properties("title").Item(0))         MsgBox(oResult.Properties("department").Item(0))         MsgBox(oResult.Properties("physicalDeliveryOfficeName").Item(0))         MsgBox(oResult.Properties("telephoneNumber").Item(0))         'You can get the Directory Entry by calling         Dim oEntry As System.DirectoryServices.DirectoryEntry = _             oResult.GetDirectoryEntry         MsgBox(oEntry.Path)     Next oResult Else     MsgBox("You must enter an LDAP path!", vbOKOnly + vbExclamation) End If 

Reading and Writing Entries

Instead of searching for entries in the directory, if you know the path of the entry you want to view, you can use the DirectoryEntry class. This class allows you to create, read, write, rename, move, enumerate, or delete children in the directory. The first thing to do when you create your DirectoryEntry instance is to either pass the path to the constructor of the directory object you want to open or pass nothing to the constructor. If you pass nothing to the constructor, the Active Directory Locator Service will help you find the right directory server to work with.

You can also navigate through the directory hierarchy by using the DirectoryEntry class. You can get the parent of the current node by using the Parent property. You can get the children by using the Children property, which returns a collection containing the children. If you want to find a specific child, you can use the Find method and pass along the directory identifier you want to use to find the specified child, such as cn=Username .

The DirectoryEntry object also provides access to the properties contained in the directory object it is associated with. You can use the Properties collection to view specific properties contained in the directory object. Because this is a collection, it can hold a single value or multiple values. When you use the Value property on the Properties collection, this is the same thing as asking for the first element in the collection, such as object.Properties.Item(0).value .

Sometimes you might want to modify a property. To do this, you just set the property to the new value. If you simply want to add a value, use the AddRange method. Be sure to call CommitChanges after modifying the properties.

To add an entry, you just call the Add method and pass the name of the new entry and the schema class name for the new entry. This method returns a DirectoryEntry object. Set the appropriate mandatory properties according to the directory service you are using, and then call CommitChanges .

To remove an entry, you just call the Remove method and pass the name of the child you want to remove. If you want to remove an entire tree, call the DeleteTree method.

The following sample shows you how to use the DirectoryEntry object to read and write directory properties:

 Try     Dim oDE As New _         System.DirectoryServices.DirectoryEntry(txtLDAPPath.Text)     'You could also use either of these:     'Dim oDE As New _     '    System.DirectoryServices.DirectoryEntry(txtLDAPPath.Text, _     '    "username", "password")     'Dim oDE As New _     '    System.DirectoryServices.DirectoryEntry(txtLDAPPath.Text, _     '    "", "", System.DirectoryServices.AuthenticationTypes.Secure)          'Msgbox some properties     MsgBox(oDE.Properties("cn").Value)     MsgBox(oDE.Properties("title").Value)          'Get the children     Dim oDEChildren As System.DirectoryServices.DirectoryEntries     oDEChildren = oDE.Children()          Dim oDEChild As System.DirectoryServices.DirectoryEntry     For Each oDEChild In oDEChildren         MsgBox(oDEChild.Properties("cn").Value)     Next          'You can also find specific children using Find()     'Find a user with cn=Thomas Rizzo     Dim oTRDE As System.DirectoryServices.DirectoryEntry = _         oDEChildren.Find("CN=Thomas Rizzo")     MsgBox(oTRDE.Properties("cn").Value)     'Modify a property     oTRDE.Properties("title").Value = "King of the World!"     'Need to commit changes!     oTRDE.CommitChanges()          'Example: Create a new entry     Dim oBGDE As System.DirectoryServices.DirectoryEntry = _         oDEChildren.Add("CN=Bill Gates", "user")     'Set the appropriate mandatory properties defined by the     'directory service     oBGDE.Properties("X").Value = "y"     'Call CommitChanges     oBGDE.CommitChanges() Catch     MsgBox("There was an error.  " & Err.Number & " " & Err.Description) End Try 

Calling Native ADSI Interfaces

Not all ADSI functions are exposed via System.DirectoryServices . This means that sometimes you might need to call directly to the previous version of ADSI. You don't have to include both libraries in your application, however ”the DirectoryEntry class provides an Invoke method that allows you to call the nonmanaged version of ADSI to perform your function. For example, DirectoryEntry does not expose the SetPassword method, but ADSI does. Using the Invoke method, as shown in the following code, you can call the SetPassword method in ADSI:

 Dim oDE As New System.DirectoryServices.DirectoryEntry(txtLDAPPath.Text) 'Need to pass a param array with the call for any parameters 'to the ADSI call Try     Dim oObject(0) As Object     oObject(0) = "NewPassword"     oDE.Invoke("SetPassword", oObject) Catch     MsgBox("Error! " & Err.Number & " " & Err.Description) End Try 

Disposing of Connections

When you work with the DirectoryServices namespace, connections to the directory are cached if you are accessing the same server with the same username, password, and port number. If you no longer need the connection, you should implicitly call the Dispose method to close the connection and free the resources used by the connection.

Authenticating Against Active Directory Using Forms-Based Authentication

When you work with the DirectoryServices library, you might want to use .NET forms-based authentication and allow your program to pass a username and a password for authentication against Active Directory. The following C# code shows how to do this:

 public bool IsUserValid(String uid, String pwd) {    DirectoryEntry entry = new DirectoryEntry(_path, uid, pwd,       AuthenticationTypes.Delegation);         try    {       //Bind to the native AdsObject to force authentication.       Object obj = entry.NativeObject;    }    catch (System.Runtime.InteropServices.COMException e)    {       //Something happened. You will need to write code to see if it is       //an Access Denied e.ErrorCode or something else.       throw new Exception(ex.Message);    }    return true; } 



Programming Microsoft Outlook and Microsoft Exchange 2003
Programming MicrosoftВ® OutlookВ® and Microsoft Exchange 2003, Third Edition (Pro-Developer)
ISBN: 0735614644
EAN: 2147483647
Year: 2003
Pages: 227
Authors: Thomas Rizzo

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