Directory Synchronization Queries

It is a fact of life that there is no one source of truth for all data in most enterprises of any size. As such, we often need to build applications that integrate various sources of data through synchronization processes.

In both Active Directory and ADAM, we can track changes to the directory using an LDAP extension called the DirSync control. This extension allows a user to return the difference between the initial state of the directory and some later state. To do this, an initial search is performed that records the current state into a cookie. On a subsequent search, this cookie is handed back and is used to determine which objects have changed, and an updated cookie is returned. Since an object change could include the actual deletion of an object, this type of search automatically includes tombstones.

Warning: Some DirSync Options Require Windows 2003 Clients!

As of this writing, some parts of the ADSI support for DirSync require a Windows Server 2003 client. Please refer to the sidebar DirectorySynchronizationOptions, later in this chapter, for additional details.

 

Limitations on Search Root and Scope

We must use the root of one of our directory or application partitions as the SearchRoot (application, domain, configuration, or schema). The only scope allowed for this type of search is Subtree. Typically, we would use paging for a large Subtree-scoped search. However, in this type of search, paging is not allowed.

Permissions

To use this type of search, the caller must have the "directory get changes" privilege (SE_SYNC_AGENT_NAME) for the current root directory partition being searched. By default, this means only an administrator or SYSTEM will be able to perform this search. Finally, the caller must have the DS-Replication-Get-Changes control access right (which is a special type of entry applied to the security descriptor on the partition root object). As we can see, this prevents all but the most privileged users from using this functionality. These privileges are needed specifically because in essence, we are allowing the running account to view any changed attribute on every object, regardless of the underlying access control list (ACL) permission.

For Windows 2003, we have the option of using DirectorySynchronizationOption.ObjectSecurity, which allows the current user to see the changes on objects that she would already have permission to see based on her current security context. Objects that the current security credentials would not normally be allowed to see will not be returned. This is a good way to give this ability to less-privileged accounts and still allow this type of search.

By default, we should be aware that no ADAM security principal will have the DS-Replication-Get-Changes control access right by default. This will have to be added to a group or user using a tool such as ADAM's version of dsacls.exe.

Filter

Any valid filter is usable; however, we have to keep in mind that only the objects that match that filter will be tracked on the initial search and on subsequent searches. For instance, if we specified (sn=Dunn) for the initial search, only objects with a last name equal to "Dunn" would be tracked for subsequent searches. Instead, if we initially specified (objectClass=user), all users would be tracked (as well as computers). A subsequent search to find changes could specify (sn=Dunn), which would filter the results equivalently to using (&(objectClass=user)(sn=Dunn)).

Attributes

We can specify specific attributes we are interested in tracking by adding them to the PropertiesToLoad collection. If we pass null (Nothing in Visual Basic .NET), this is an indication that we wish to track all attributes. Any specified attributes in the PropertiesToLoad collection will also act as an additional filter, as only those objects on the initial search that have at least one of these attributes populated will be tracked. Subsequent searches will include only those objects where at least one of the tracked attributes has been updated. When inspecting the returned SearchResult, only the attributes that have changed will be included, regardless of whether the attribute is populated. In addition to any changed attributes, ADsPath, objectGuid, and instanceType will always be included in a SearchResult.

DirSync Samples

Listing 5.5 demonstrates a complete DirSync implementation.

Listing 5.5. Sample DirSync Class

public class DirSync
{
 //snip... just the juicy parts left for brevity

 public void InitializeCookie(string qry)
 {
 //this is our searchroot
 DirectoryEntry entry = new DirectoryEntry(
 this.adsPath,
 this.username,
 this.password,
 AuthenticationTypes.Secure
 );

 using (entry)
 {
 //we want to track all attributes (use null)
 string[] attribs = null;

 DirectorySearcher ds = new DirectorySearcher(
 entry,
 qry,
 attribs
 );

 //we must use Subtree scope
 ds.SearchScope = SearchScope.Subtree;

 //pass in the flags we wish here
 DirectorySynchronization dSynch = new
 DirectorySynchronization(
 DirectorySynchronizationOptions.None
 );
 ds.DirectorySynchronization = dSynch;

 using (SearchResultCollection src=ds.FindAll())
 {
 Console.WriteLine(
 "Initially Found {0} objects",
 src.Count
 );

 //get and store the cookie
 StoreCookie(
 dSynch.GetDirectorySynchronizationCookie()
 );
 }
 }
}

public void GetSynchedChanges(string qry, bool saveState)
{
 //this is our searchroot
 DirectoryEntry entry = new DirectoryEntry(
 this.adsPath,
 this.username,
 this.password,
 AuthenticationTypes.Secure
 );

 using (entry)
 {
 string[] attribs = null;

 DirectorySearcher ds = new DirectorySearcher(
 entry,
 qry,
 attribs
 );

 //we must use Subtree scope
 ds.SearchScope = SearchScope.Subtree;

 //pass back in our saved cookie
 DirectorySynchronization dSynch = new
 DirectorySynchronization(
 DirectorySynchronizationOptions.None,
 RestoreCookie()
 );

 ds.DirectorySynchronization = dSynch;

 using (SearchResultCollection src=ds.FindAll())
 {
 Console.WriteLine(
 "Subsequently Changed: {0} objects",
 src.Count
 );

 //return each object that has changed
 //and what attributes have changed,
 //keeping in mind that the attributes:
 //'objectGuid', 'instanceType', and
 //'ADsPath' will always be returned as well
 foreach (SearchResult sr in src)
 {

 Console.WriteLine(
 "Detected Change in {0}",
 sr.Properties["AdsPath"][0]
 );

 Console.WriteLine("Changed Values:");
 Console.WriteLine("==============:");

 foreach (string prop in
 sr.Properties.PropertyNames)
 {
 Console.WriteLine(
 "	 {0} : {1}",
 prop,
 sr.Properties[prop][0]
 );
 }
 }

 if (saveState)
 {
 //get and store the cookie again
 StoreCookie(
 dSynch.GetDirectorySynchronizationCookie()
 );
 }
 }
 }
 }

 //quick method to save the byte[] array to disk
 private void StoreCookie(byte[] cookieBytes)
 {
 //snipped for brevity!
 }
 //quick method to restore the byte[] array from disk
 private byte[] RestoreCookie()
 {
 //snipped for brevity!
 }
}

The class shown in Listing 5.5 has been abbreviated to cut down on the size of this book, but we provide a complete listing on the book's web site.

Our sample class can be consumed using a technique similar to that shown in Listing 5.6.

Listing 5.6. Demonstrating DirSync Class Use

//optionally add credentials if not running
//with privileges
DirSync ds = new DirSync();

//initialize our search and look for all users
ds.InitializeCookie(
 "(objectClass=user)"
 );

//..update the directory somehow

//now retrieve what has changed
//this can be called much later
//or in another program as state
//is stored to the local drive
ds.GetSynchedChanges(
 "(objectClass=user)",
 true
 );

It is important to note that we are essentially comparing two snapshots in time. Actually, that is not entirely trueit is more as though we are viewing a partial snapshot full of changes since a given point in time. We actually do not know the initial values of any object directly. We know only when something has changed, and we know its final value. We should also note that object state can differ between domain controllers, even at the same moment in time. As such, it is advisable to bind to the exact same domain controller each time when comparing snapshots. This eliminates the natural discrepancy that can occur between object states based on localized but nonreplicated changes.

One last point that bears mentioning is that we really have no way of tracking any of the intermediate changes that might have occurred between an object's initial state and its final state right before the search. An object's state can vary drastically between its initial and final states, but these intermediate values will not be captured by this type of search. Only the final state of the object will be directly viewable in these cases.

As we can see, the DirectorySynchronization class is extremely powerful if we can live with some of the limitations imposed on us.

DirectorySynchronizationOptions

The DirectorySynchronizationOptions enumeration represents additional settings we can specify for a search. The possible values are as follows.

  • IncrementalValues. Supported only on Windows 2003 server. If this flag is specified, only the changed values from a multivalued attribute (up to the limit) will be returned.
  • None. This is the default flag setting.
  • ObjectSecurity. Supported only on Windows Server 2003. If not specified, the user must have the replicate changes privilege (SE_SYNC_AGENT_NAME). When present, the user can view objects and attributes accessible to the caller.
  • ParentsFirst. Return the parent object before the children if both have changed.
  • PublicDataOnly. Private, nonreplicated data is omitted from the results.


Using Attribute Scope Query

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