Whistler

The next version of Windows 2000, code-named "Whistler," is in beta testing as of this writing. The server editions of Whistler will contain a new release of Active Directory that is expected to contain a number of enhancements and new features. Here is a partial list of what Whistler is expected to include as far as ADSI and Active Directory support is concerned:

  • Dynamic objects and dynamic auxiliary classes  Provides support for directory objects that have a specified lifetime.
  • Application Directory Partitions  New partitions (or naming-contexts) can be created within Active Directory that have their own replication properties and schedule.
  • A new object class named inetOrgPerson  This new class, defined in RFC 2798, allows for better interoperability between Active Directory and third-party LDAP directories and also provides secure validatation of e-commerce customers.
  • Virtual list view (VLV) searches  VLV searches instruct Active Directory to return a portion of an entire result set.
  • Attribute Scoped Query searches  Attribute Scoped Query searches return objects referenced in a multivalued attribute stored in the base object.
  • Ability to turn off referral generation by the server  By using the extended LDAP control LDAP_SERVER_DOMAIN_SCOPE_OID when a search is conducted against Active Directory in Whistler, the server will not bother to generate referrals. In most cases referrals will be ignored by the client, so this can improve search performance. ADSI will automatically use this extended control when searches specify the ADS_CHASE_REFERRALS_NEVER flag.
  • Ability to reverse, or roll-back, schema modifications
  • Improved universal groups  Groups are no longer limited to 5,000 members. In addition, replication issues are eased by replicating group membership changes on a per-value basis rather than a per-attribute basis. However, this improvement requires that all the domain controllers in a forest are running Whistler.
  • User interface improvements  Whistler includes a radical change to the object picker dialog box that makes it more efficient for large networks. Administrators can pick multiple objects in the snap-ins and edit them as a group. Also, common queries such as finding printers can be saved.
  • New security protocols  LDAP, and thus ADSI, now support the TLS and Digest Authentication (DIGEST-MD5 SASL) methods for better integration with the Windows security system. Additionally, the default security for many directory objects is increased

I'd love to discuss all of these new features in depth, but they are still being tested, and how they appear in the final product might be different from how I've described them above. However, I do want to cover a few of these changes in more detail—dynamic objects, application partitions, the inetOrgPerson class, virtual list view searching, and user interface enhancements.

Dynamic Objects

At the beginning of this book, I mentioned that directory data is relatively static. Directories traditionally have been poor hosts to data that is changed frequently or has a limited lifetime. With the release of Whistler, however, Active Directory will provide more support for dynamic data using a feature named dynamic objects. By using a new auxiliary class, you can create a class that contains a time-to-live (TTL) value for a directory object. The TTL specifies, in seconds, how long the particular instance of the object should remain in the directory. When the TTL reaches zero, the instance is deleted.

The network service that requires the dynamic data is, in effect, off-loading garbage collection to Active Directory. A good example of this might be a DHCP address lease. When a networked computer requests a TCP/IP address, the DHCP server could create a dynamic object that contains information about the address being leased and set an expiration value. Unless the machine refreshed the lease, and the DHCP server in turn refreshed the dynamic object, the object would be deleted. Clients could refresh the TTL by directly updating the attribute or by performing an LDAP extended operation.

Classes of dynamic objects are created by including a new auxiliary class in the schema when the class is created. This auxiliary class is called dynamicObject. This auxiliary class adds an optional attribute to the newly created class called entryTTL, which is the time-to-live value expressed in seconds. Since it's impractical to have Active Directory update the value of this attribute every second, entryTTL is implemented as a constructed attribute, meaning that its value is computed. When a client accesses entryTTL, Active Directory looks at another attribute of the dynamicObject class, ms-DS-Entry-Time-To-Die, which is the absolute time for when the object will be deleted (unless refreshed). Active Directory then subtracts the current time from the absolute time and returns the difference as the number of seconds.

Not only can new classes be dynamic, but existing objects can be made dynamic as well. You do this by adding the dynamicObject auxiliary class name to the existing object's objectClass attribute. At the same time, you also set the value for the TTL.

Dynamic objects are much like ordinary objects with regard to searching or enumeration. A minor difference is that when the TTL expires and the object is deleted by Active Directory, no tombstone is left behind. Also, you can create dynamic container objects, but they may contain only dynamic objects. This makes sense because you wouldn't want Active Directory deleting your static directory object just because the dynamic container got blown away.

While the dynamicObject auxiliary class is usually added to make a limited-lifetime object, Active Directory in Whistler will allow any auxiliary class to be attached to an existing object. This feature is known as dynamic auxiliary classes. The new auxiliary class is added as a value to the objectClass attribute. To keep track of just the structural classes, a new optional attribute, structuralObjectClass, is defined as an optional attribute of the top class. This attribute contains nothing but the names of the structural classes that define the object.

Refreshing Dynamic Objects

The TTL value for dynamic objects can range from 1 second to 1 year. However, the object can stay in the directory indefinitely as long as it's refreshed before the TTL value is 0 and the object expires. The object is refreshed either by directly modifying the entryTTL attribute with a new value or by using an extended LDAP control.

You can find out whether the server you're communicating with can refresh dynamic objects by checking the supportedExtension attribute of the RootDSE object. This multivalued attribute lists the extended operations that the server supports. The dynamic refresh operation is listed with the object identifier (OID) of 1.3.6.1.4.1.1466.101.119.1. When the object is refreshed using the extended operation, a value is sent from the client to the server specifying the new TTL. As stipulated in RFC 2589, however, the server can reject the client-specified value and return instead a value that it assigns. A rejection such as this might occur because of the server's workload or because the TTL value requested by the client is outside the minimum and maximum limits defined for the server.

More information about dynamic objects can be found in RFC 2589, "Lightweight Directory Access Protocol (v3): Extensions for Dynamic Directory Services."

Application Partitions

I bet you're thinking about the replication impact of dynamic data. Wouldn't the constant creation and deletion of short-lived objects generate a lot of replication traffic? Generally, yes, but Whistler also includes a feature named application partitions (also known as non-domain naming contexts, or NDNC) that can be used to cordon off dynamic data from more traditional and static directory data.

In Whistler, you cannot create dynamic objects in the configuration or the schema partition. This limitation makes perfect sense because those partitions replicate to all the domain controllers in the forest. You wouldn't want data that changes frequently to be replicated to hundreds of domain controllers, often in remote locations and connected by slow links.

By using application partitions, a directory-enabled application can store static or dynamic data and finely control or prevent its replication throughout the forest. This allows data to be located only where it's needed, reducing the effect of replication traffic on the network. The use of an application partition is particularly well suited to dynamic data that changes more often than the average replication latency of the configuration partition. You can still take advantage of Active Directory's replication infrastructure, but you set the rules.

An application partition can contain any type of object with the exception of security principal objects such as users, groups, or computers. Windows must ensure that those kinds of objects are available throughout the network. Objects in application partitions are also not replicated to the global catalog.

inetOrgPerson

Active Directory in Whistler includes support for the common inetOrgPerson class. Many early adopters of Active Directory were users of other directory products, such as Netscape Directory Server and Novell NDS. Both of these products, as well as other third-party LDAP directories, supported the inetOrgPerson class that was used to keep track of people with access to networking resources. While Active Directory has the organizationalPerson class, this class was an abstract class, and objects could not be created from it.

To ease the burden of migrating from and synchronizing with another directory service, the Whistler release of Active Directory implements the inetOrgPerson class. The inetOrgPerson class is defined in RFC 2798, which is an informational RFC, not a mandatory one. All the attributes defined in RFC 2798 are included as part of the Active Directory inetOrgPerson class. Table 11-2 lists those attributes.

inetOrgPerson Attribute Syntax Defined In Description

audio

Octet string

RFC 1274

An audio recording. Format is not defined. Multivalued.

businessCategory

Directory string

RFC 2256

The category of business for this person. Multivalued.

carLicense

Directory string

RFC 2798

The license or registration plate associated with the person's vehicle. Multivalued.

departmentNumber

Directory string

RFC 2798

Numeric or alphanumeric code for the department to which a person belongs. Multivalued.

displayName

Directory string

RFC 2798

Printable version of the person's name.

employeeNumber

Directory string

RFC 2798

Numeric or alphanumeric identifier assigned to a person.

employeeType

Directory string

RFC 2798

Identifies the employer-to- employee relationship; for example, "full time," "contractor," "intern," and so on.

givenName

Directory string

RFC 2256

The first name of the person.

homePhone

Directory string

RFC 1274

The home telephone number for the person.

homePostalAddress

Directory string

RFC 1274

The postal mailing address for the person.

initials

Directory string

RFC 2256

Active Directory uses this attribute for the person's middle initial, although it can be used for the combination of the first letters of each name of the user.

jpegPhoto

Octet string

RFC 2798

Image of the person in JPEG File Interchange Format (JFIF). Multivalued.

labeledURI

Directory string

RFC 2079

Uniform Resource Identifier (URI) with a label. Multivalued.

mail

Directory string

RFC 2798

The e-mail address of the person.

manager

Distinguished name

RFC 1274

The distinguished name of the object associated with the manager or supervisor of the person.

mobile

Directory string

RFC 1274

The phone number for the mobile telephone for the person.

o

Directory string

RFC 2256

The name of the organization associated with the person. Multivalued.

pager

Directory string

RFC 1274

The phone number of the pager device used by the person.

photo

Octet string

RFC 1274

Photograph of the person in G3 fax format. Multivalued.

preferredLanguage

Directory string

RFC 2798

The person's preferred written or spoken language. Values for this attribute conform to the Accept- Language header field defined by HTTP in RFC 2068.

roomNumber

Directory string

RFC 1274

The room number associated with the person's primary location. Multivalued.

secretary

Distinguished name

RFC 1274

The distinguished name of the object representing the secretary or assistant to the person. Multivalued.

uid

Directory string

RFC 2798

The user identification used for computer network access. Multivalued.

userCertificate

Octet string

RFC 2256

A security certificate for the person. Format is undefined. Multivalued.

userPKCS12

Octet string

RFC 2798

Contains the person's personal identity information in the Public Key Cryptography Systems #12 (PKCS #12) standard format. Multivalued.

userSMIMECertificate

Octet string

RFC 2798

The person's S/MIME certificate encoded with PKCS #7 (RFC2315) standard format. Multivalued.

x500UniqueIdentifi er

Octet string

RFC 2256

Used to distinguish between objects when a distinguished name has been reused. Multivalued.

Table 11-2 Attributes that are defined for the new inetOrgPerson class in Whistler. This table does not include attributes that are inherited from other classes.

An important feature of Active Directory implementation of the inetOrgPerson class is that it inherits from the user class. Since the user class includes the securityPrincipal auxiliary class, this allows inetOrgPerson objects to be equal to other users with respect to security and access control. Having this equality is particularly important with e-commerce applications that must securely authenticate remote users to allow access to databases and process transactions. Now customers can be represented with inetOrgPerson classes and can be authenticated using the same security methods available to intranet and LAN users.

There is no need to move all your users to the new inetOrgClass because Whistler adds the same attributes to its version of the user class. This helps retain compatibility with existing deployments of Active Directory.

Virtual List View Searching

The release of Active Directory in Whistler supports a new type of searching called virtual list view (VLV). A VLV search relies on the directory server to provide a virtual "view" into the entire result set. The server manages the data, while the client retrieves only the portion of the result set that is currently displayed to the user.

This mechanism is similar in nature to the virtual list-view style for list-view controls. It's best used for address box style applications in which a search can return many hundreds or thousands of results. Normally, to indicate to the user the relative position of the entries shown, the client needs to know the total number of results from a particular search. In a VLV search, the search can quickly return an estimate of the total number of entries along with the first result set so that the user can gauge the current view in relation to the total.

Imagine you are creating an address book application for an organization with thousands of people listed in the directory. The user invokes the application, which shows the first dozen or so people returned. Now imagine that the user wants to quickly move to the middle of the list to see entries that start with the letter O. In most list views, the user can press the O key and that event will move the focus to the first item starting with O.

Without a VLV search, the application would have to do one of two things: it could abandon the current search and request a new one with all the entries starting with O, or the application could retrieve all the entries (remember, there are thousands) and cache them locally. Doing the latter allows the user to move quickly between the various entries but only after the server has returned A through Z, which could take an unacceptably long time.

The VLV search provides the benefits of both methods without the network traffic and delay associated with returning all the results to the client. The client also doesn't have to allocate large amounts of memory to cache the results. However, the server does return an estimate of the total number of results, so the application appears to have all the results available and the scroll bar or slider can indicate the relative position of the current view within the entire result set.

To provide support for VLV searches, Whistler defines two LDAPv3 extended controls, which are additions to the LDAP API and protocol that support new functionality. Both controls contain information about the view.

The process of performing a VLV search is similar to a regular search using the IDirectorySearch interface. You supply an ADS_VLV structure, which is placed in the ADS_SEARCHPREF_INFO array. You must also specify a sorted result using the ADS_SORTKEY structure.

The ADS_VLV structure specifies how many rows to return in the search and at what offset. For example, if you searched a container that has 1,000 items, but you only want the first 20 to be returned, the dwOffset member would be zero, and the dwAfterCount would be 20. VLV also allows you to guess at the number of items you need to view in a result set. If you don't know how many items the search will return, but you want to show the middle 50, you can indicate to Active Directory that you think 100 items are part of the search, and you want the offset to be 50. Active Directory will take the ratio you supply (100/50) and apply it to the estimate that the server comes up with. It may not be exact, but if the folder contains 1200 items, the search offset will be 600.

Similarly, if you want to display the last 10 items of a container, set your estimate to 100 (or any positive value) and set the offset to the same value. Then put the value 10 into dwBeforeCount so that Active Directory will return 10 rows.

When you're ready to retrieve the results, execute the search normally using the ExecuteSearch method of the IDirectorySearch interface. However, not until the GetFirstRow method is called or the GetNextRow method is called for the first time is the server contacted and a VLV response returned along with the number of results you specify (dwBeforeCount + dwAfterCount + the target).

The VLV response is returned as a column. The server returns a pointer to the ADS_VLV structure that is updated with an estimated count of the total number of results and the adjusted offset. The application can then apply a new offset and perform the search again. Since it's only a few results at a time, the server can process it very quickly.

Listing 11-4 uses the VLV searching method in Whistler to return all the objects in the schema container. The search starts at the end of the container and displays the records in blocks of 40, each moving backward until all the items have been shown.

The code below is based on the Beta 2 version of Whistler and may not function correctly or at all in the final version.

 //--------------------------------------------------------------
// VLVSearch.cpp
//
// Description:
// Display the contents of the schema container in blocks of 40
// items, going backward.  Shows how virtual list view searches
// work under Whistler.
//
// Platform: Win32 Console Mode Application
//--------------------------------------------------------------
// C++ and Compiler Support
#include <tchar.h>
#include <string.h>
#include <stdio.h>
#include <comdef.h>
// Windows Platform Support
#include <objbase.h>
// Active Directory Support
#include <prerelease\activeds.h>
// Warning level 4 for this code
#pragma warning( push, 4 )
// Includes ADSI libraries
#pragma comment( lib, "activeds.lib" )
#pragma comment( lib, "adsiid.lib"   )
// Returns number of elements in array (good for strings)
#define ARRAYSIZE(a)    (sizeof(a)/sizeof(a[0]))
// Function prototype
HRESULT VLVSearchAndDisplay(IDirectorySearch* padsSearch,
    PADS_VLV padsVLVPref );
//----------------------------------------------------------------
// Function:  wmain
// Inputs:    int argc        Number of command line arguments
//            wchar_t *argv[] Pointer to argument string array
//            wchar_t *envp[] Pointer to enviroment string array
// Returns:   int             Program exit code 
//                            0 = no errors, otherwise HRESULT
//---------------------------------------------------------------- int wmain( int /*argc*/, wchar_t* /*argv[]*/, wchar_t* /*envp[]*/)
{
//Initialize COM
HRESULT hResult = CoInitialize(NULL);
// Get a base IADs object
IADs *padsRootDSE;
hResult = ADsGetObject( L"LDAP://RootDSE",
                        IID_IADs,
                        (void**) &padsRootDSE );
// Use the Get method to get the schema partition
_variant_t varDomain;
hResult = padsRootDSE->Get(L"schemaNamingContext", 
                           &varDomain);
//Build LDAP path to the domain
_bstr_t bstrDirectoryPath =_bstr_t( "LDAP://" ) +
                           _bstr_t( varDomain );
_tprintf( _T("Searching %ls\n"), (wchar_t*)bstrDirectoryPath);
// Get the directory search interface to the domain
IDirectorySearch *padsSearchContainer = NULL;
hResult = ADsGetObject( bstrDirectoryPath,
                        IID_IDirectorySearch, 
                        (void **) &padsSearchContainer );
// Allocate space for the VLV preferences
// Recommend initializing all members to zero
ADS_VLV adsVLVPref = { NULL };
// Show one block at a time (before + target + after)
DWORD dwBlockSize = 40;
bool bLastBlock = false;
// Set the initial VLV preferences
adsVLVPref.dwContextIDLength = 0;
adsVLVPref.lpContextID = NULL;
// Initial estimate of number of entries 
adsVLVPref.dwContentCount = 100;
// Start at the end
adsVLVPref.dwOffset = adsVLVPref.dwContentCount; adsVLVPref.dwBeforeCount = dwBlockSize;
adsVLVPref.dwAfterCount = 1;
// Loop backward through the container
while (true)
    {
    _tprintf( _T("---Offset %d of %d---\n"), 
              adsVLVPref.dwOffset,
              adsVLVPref.dwContentCount );
    // Get the next block
    hResult = VLVSearchAndDisplay( padsSearchContainer, &adsVLVPref );
    // Move backward through the container (with overlap)
    adsVLVPref.dwOffset -= dwBlockSize;
    // Less than zero?  Go once more with 0 as offset
    if ((signed int)adsVLVPref.dwOffset < 0)
        {
        if (bLastBlock = false)
            {
            bLastBlock = true;
            adsVLVPref.dwOffset = 0;
            adsVLVPref.dwBeforeCount = 0;
            adsVLVPref.dwAfterCount = dwBlockSize;
            }
        else
            // If last block was set, break out of loop
            break;
        }
    }
// Clean up objects
if ( padsSearchContainer )
    padsSearchContainer->Release();
if ( padsRootDSE )
    padsRootDSE->Release();
// Uninitialize COM
CoUninitialize();
return hResult;
} //----------------------------------------------------------------
// Function: VLVSearchAndDisplay
// Inputs:   IDirectorySearch* Object reference to search container
//           PADS_VLV          VLV search preferences, updated on exit
// Returns:  HRESULT           Program exit code 
//                             0 = no errors, otherwise HRESULT
//----------------------------------------------------------------
HRESULT VLVSearchAndDisplay(IDirectorySearch* padsSearch,
                            PADS_VLV padsVLVPref )
{
HRESULT hResult;
// Create search preferences structure
ADS_SEARCHPREF_INFO arSearchPrefs[3];
// Set the VLV search preferences into the array
arSearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_VLV;
arSearchPrefs[0].vValue.dwType = ADSTYPE_PROV_SPECIFIC;
arSearchPrefs[0].vValue.ProviderSpecific.dwLength = sizeof( ADS_VLV );
arSearchPrefs[0].vValue.ProviderSpecific.lpValue = (LPBYTE)padsVLVPref;
// Set a subtree search
arSearchPrefs[1].dwSearchPref   = ADS_SEARCHPREF_SEARCH_SCOPE;
arSearchPrefs[1].vValue.dwType  = ADSTYPE_INTEGER;
arSearchPrefs[1].vValue.Integer = ADS_SCOPE_SUBTREE;
// Create a sort key using the cn attribute
ADS_SORTKEY adsSortKey = { NULL };
adsSortKey.pszAttrType = L"cn";
adsSortKey.fReverseorder = 0;
// Create the search sort search preference
arSearchPrefs[2].dwSearchPref = ADS_SEARCHPREF_SORT_ON;
arSearchPrefs[2].vValue.dwType = ADSTYPE_PROV_SPECIFIC;
arSearchPrefs[2].vValue.ProviderSpecific.dwLength = sizeof(ADS_SORTKEY);
arSearchPrefs[2].vValue.ProviderSpecific.lpValue = (LPBYTE) &adsSortKey;
// Set the search preferences
hResult = padsSearch->SetSearchPreference( arSearchPrefs, 3 );
// Execute search and request all attributes
// Returns handle to search
ADS_SEARCH_HANDLE hSearch;
hResult = padsSearch->ExecuteSearch( L"(objectClass=*)",
                                     NULL,
                                     -1,
                                     &hSearch); if ( SUCCEEDED(hResult) )
    {
    // GetFirstRow to actually run the search
    hResult = padsSearch->GetFirstRow(hSearch);
    if (SUCCEEDED(hResult))
        {
        ADS_SEARCH_COLUMN colSearchColumn;
        // Loop through each row returned
        while (hResult != S_ADS_NOMORE_ROWS)
            {
            // Get first attribute 
            hResult = padsSearch->GetColumn(hSearch,
                                            L"Name", 
                                            &colSearchColumn);
            if (SUCCEEDED(hResult))
                {
                // Display the attribute
                _tprintf( _T("%ls\t\n"),
                          colSearchColumn.pADsValues->CaseIgnoreString);
                // Must free the column each time
                padsSearch->FreeColumn( &colSearchColumn );
                }
            //Get the next row
            hResult = padsSearch->GetNextRow( hSearch );
            }
        // Get the VLV response metadata as a column
        hResult = padsSearch->GetColumn(hSearch, 
                                        ADS_VLV_RESPONSE, 
                                        &colSearchColumn );
        // Verify that it is VLV data
        // ADsType is set to ADSTYPE_PROV_SPECIFIC
        // and the number of values is 1
        if (colSearchColumn.dwADsType == ADSTYPE_PROV_SPECIFIC &&
            colSearchColumn.dwNumValues == 1 )
            {
            // Retrieve the metadata into a ADSVALUE structure
            ADSVALUE adsValue = colSearchColumn.pADsValues[0];
            PADS_VLV pVlv = (PADS_VLV)(adsValue.ProviderSpecific.lpValue);             // Update the passed in structure with the new values
            padsVLVPref->dwContentCount = pVlv->dwContentCount;
            padsVLVPref->dwOffset = pVlv->dwOffset;
            }
        else
            _tprintf( _T("Error retrieving Virtual List View Response 
                from Active Directory.\n"));
        // Must free the VLV metabase column like any other
        padsSearch->FreeColumn( &colSearchColumn );
        }
    // Close the search handle to clean up
    padsSearch->CloseSearchHandle(hSearch);
    }
return hResult;
}

Listing 11-4 VLVSearch.cpp uses the VLV searching method of Whistler to return all the objects in the schema container in blocks of 40 items.

User Interface Enhancements

Whistler also contains a number of changes to the user interface for Active Directory. A goal of the new design is to reduce network traffic for large organizations. For example, a new object picker dialog box is available that enables searches. When showing a list of users, the Windows 2000 object picker dialog box would query the server for all the users, even though only one would be required.

The Whistler version of the object picker dialog box, shown in Figure 11-6, reminds me of an e-mail interface. When searching for a name, you can type it in and click the Check Names button to resolve any ambiguities. The Advanced button displays the new query dialog box, shown in Figure 11-7. While this user interface is still being worked on, the ability to start a search from within the object picker is very powerful.

Figure 11-6 New object picker available in Whistler.

Figure 11-7 Whistler query form available from object picker.

I'm happy to report that all the user interface code presented in Chapter 8 works as expected with the Beta 2 version of Whistler.



MicrosoftR WindowsR 2000 Active DirectoryT Programming
MicrosoftR WindowsR 2000 Active DirectoryT Programming
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 108

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