The Basics of Searching

While LDAP allows us to do many things, searching is really the primary activity of LDAP programming. As such, it makes sense that we spend two whole chapters talking about it. Before we can execute a search, we need to answer some important questions.

  • Where should the search start?
  • How deep in the directory tree should the search go?
  • Which objects should be found?
  • What attribute data from each object found should be returned?
  • How many objects should be found?
  • How will security affect my search?

If we take each of these questions in turn, we can start to formulate a simple plan for how to design and perform a search in the most efficient manner. If all of this is starting to look too complicated, don't worry, it will become second nature as your experience using SDS grows.

Deciding Where to Search

Our search starts at the location in the directory defined by the SearchRoot property. SearchRoot contains a DirectoryEntry that represents both the binding context that will be used for connecting to the directory as well as the starting location for the search.

For example, let's say we want to do a search starting at the normal Users container of our Active Directory domain, and the distinguished name (DN) of the domain naming context is as follows:

DC=domain,DC=com

We would first create a DirectoryEntry object pointing to the Users container and pass that to the SearchRoot of the DirectorySearcher, as Listing 4.1 demonstrates.

Listing 4.1. Initializing the SearchRoot of the DirectorySearcher

DirectoryEntry users = new DirectoryEntry(
 "LDAP://CN=Users,DC=domain,DC=com",
 null,
 null,
 AuthenticationTypes.Secure
 );

DirectorySearcher ds1 = new DirectorySearcher(users);

//this is equivalent

DirectorySearcher ds2 = new DirectorySearcher();
ds2.SearchRoot = users;

The DirectorySearcher will search in some part of the directory tree, starting at the CN=Users container.

Note: Use the Default SearchRoot with Caution

The default value for SearchRoot is null. This has several implications of which we should be aware. First, the LDAP provider will be assumed. Next, the RootDSE of the current domain will be contacted and the default naming context will be queried to determine the starting search location (which means we are searching the entire domain). Finally, the security credentials of the current thread will be used to determine our permissions. Needless to say, these are a lot of assumptions to make, and the result might not be exactly what we wanted. For the sake of clarity, we recommend specifying a SearchRoot using either this property or one of the DirectorySearcher constructors. This makes it much easier to decipher our intent.

 

How Security Affects Searching

The binding context of the DirectoryEntry object set as our SearchRoot determines the security context for all of the related searching operations. This means that whatever credentials our SearchRoot uses will determine what our DirectorySearcher can and cannot return. Since not all accounts in the directory may have rights to see every object and attribute value, choosing different DirectoryEntry credentials for our SearchRoot may result in completely different search results.

This really comes into play when using nonprivileged accounts and searching for attributes that only more privileged accounts can normally view. In these scenarios, it will appear as though the attribute does not exist on the object. In some cases, we may not have permission to see the whole object or even list the contents of a specific container.

It is important to note that no error will occur when searching for an attribute or location for which the user does not have the proper read privileges. Instead, the returned result set will simply be empty of the prohibited objects or attributes and no indication will be given as to the reason.

One thing a developer can do to troubleshoot these types of issues is to use one of the many freely available LDAP browsers (see Appendix B for our LDAP tools list). By binding as a standard user in such a tool and browsing to an object, we will immediately see what is and is not available in our targeted context.

Controlling Depth of Search with SearchScope

The SearchScope property controls the depth of our search below the SearchRoot object in the directory hierarchy. SearchScope uses an enumeration (also called SearchScope) to define the three possible depths: Subtree, OneLevel, and Base. The default value is SearchScope.Subtree.

Subtree

The Subtree option is typically the one that most developers will use, as it will search the current SearchRoot and all children below it, including any child containers. This is the largest of the scope settings and we typically use it when we do not know where objects are located, or when we want to search for similar objects across disparate containers.

OneLevel

The OneLevel option searches all immediate children of the current SearchRoot, excluding the SearchRoot itself. Unlike the Subtree scope, descendants below the immediate children will not be included. This option is best used when we know which container holds our target objects and we want to tighten our scope.

Base

A Base-scoped search looks within the SearchRoot object itself. This scope is generally used for retrieving constructed attributes of a particular object from the directory. The search does not select any objects below the SearchRoot in the directory hierarchy. As such, we typically set the Filter property to (objectClass=*), which will match any object, as all objects by definition must have an objectClass.

We rarely use this option in SDS to retrieve constructed attributes, because the DirectoryEntry object mirrors this functionality with the RefreshCache method. Given that we need to create the DirectoryEntry anyway to populate the SearchRoot property, using RefreshCache often requires less code and is cleaner (see Chapter 6).

The question becomes, when should we use this scope? It turns out that one of our advanced searches, called an attribute scope query (see Chapter 5), requires this option. We might also use this scope to take advantage of the different data marshaling behaviors of DirectorySearcher compared to DirectoryEntry (see Chapter 6).

Figure 4.1 illustrates the three scope options. In the diagram, we see a root object with three immediate children. Two of the child objects have children as well.

Figure 4.1. Search Scope Options Illustrated


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



The. NET Developer's Guide to Directory Services Programming
The .NET Developers Guide to Directory Services Programming
ISBN: 0321350170
EAN: 2147483647
Year: 2004
Pages: 165

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