Skip to main content
IT Connect

Information technology tools and resources at the UW

Using LDAP to Enumerate Large Groups

The NETID domain has several groups that have an extremely large membership list – for example, most of the affiliation-based groups exceed 100,000 members.  If you use an application that relies on LDAP as its underlying directory protocol, you may notice some odd behavior when checking the membership of these groups.

By default, Active Directory imposes a attribute value page limit of 1500 – in short, Active Directory will return the values of that attribute in sets of 1500, and its up to the developer to page through those sets looking for the desired value.

During the development and implementation of the NETID domain, the project team encountered this scenario and developed code to work around it.  This code has been made available below.

The code samples are written in Visual Basic .NET and C#, tested against Microsoft .NET Framework v2.0.  Without modification, this function allows you to check to see whether or not a user is in a particular group, however, this code can be easily extended to populate a local cache or collection for a variety of operations.

These code samples are provided as-is, and no support will be offered.

[ C# | Visual Basic .NET ]


C#

...
 using System.DirectoryServices;
 ...

/// <summary>
 /// Determines whether or not the specified user is a member of the group.
 /// </summary>
 /// <param name="UserDN">A System.String containing the user's distinguished name (DN).</param>
 /// <param name="Group">A System.DirectoryServices.DirectoryEntry object of the target group.</param>
 private Boolean IsMemberOfLargeGroup( String UserDN, DirectoryEntry Group )
 {
 Boolean userFound = false;
 Boolean isLastQuery = false;
 Boolean exitLoop = false;
 Int32 rangeStep = 1500;
 Int32 rangeLow = 0;
 Int32 rangeHigh = rangeLow + ( rangeStep - 1 );
 String attributeWithRange;

DirectorySearcher groupSearch = new DirectorySearcher( Group );
 SearchResult searchResults;

groupSearch.Filter = "(objectClass=*)";

do
 {
 if( !isLastQuery )
 attributeWithRange = String.Format( "member;range={0}-{1}", rangeLow, rangeHigh );
 else
 attributeWithRange = String.Format( "member;range={0}-*", rangeLow );

groupSearch.PropertiesToLoad.Clear();
 groupSearch.PropertiesToLoad.Add( attributeWithRange );

searchResults = groupSearch.FindOne();
 groupSearch.Dispose();

if( searchResults.Properties.Contains( attributeWithRange ) )
 {
 if( searchResults.Properties[ attributeWithRange ].Contains( userDN ) )
 userFound = true;

if( isLastQuery )
 exitLoop = true;
 }
 else
 {
 isLastQuery = true;
 }

if( !isLastQuery )
 {
 rangeLow = rangeHigh + 1;
 rangeHigh = rangeLow + ( rangeStep - 1 );
 }
 }
 while( ! ( exitLoop | userFound ) );

return userFound;
 }

Visual Basic .NET

...
 Imports System.DirectoryServices
 ...

/// <summary>
 /// Determines whether or not the specified user is a member of the group.
 /// </summary>
 /// <param name="UserDN">A System.String containing the user's distinguished name (DN).</param>
 /// <param name="Group">A System.DirectoryServices.DirectoryEntry object of the target group.</param>
 Private Function IsMemberOfLargeGroup( ByVal UserDN As String, ByVal Group As DirectoryEntry ) As Boolean

Dim userFound As Boolean = False
 Dim isLastQuery As Boolean = False
 Dim exitLoop As Boolean = False
 Dim rangeStep As Int32 = 1500
 Dim rangeLow As Int32 = 0
 Dim rangeHigh As Int32 = rangeLow + (rangeStep - 1 )
 Dim attributeWithRange As String

Dim groupSearch As New DirectorySearcher( Group )
 Dim searchResults As SearchResult

groupSearch.Filter = "(objectClass=*)"

Do
 If Not isLastQuery Then
 attributeWithRange = String.Format( "member;range={0}-{1}", rangeLow, rangeHigh )
 Else
 attributeWithRange = String.Format( "member;range={0}-*", rangeLow )
 End If

groupSearch.PropertiesToLoad.Clear()
 groupSearch.PropertiesToLoad.Add( attributeWithRange )

searchResults = groupSearch.FindOne()
 groupSearch.Dispose()

If ( searchResults.Properties.Contains( attributeWithRange ) ) Then
 If ( searchResults.Properties[ attributeWithRange ].Contains( userDN ) ) Then
 userFound = true
 End If

If isLastQuery Then
 exitLoop = true
 End If
 Else
 isLastQuery = true
 End If

If Not isLastQuery Then
 rangeLow = rangeHigh + 1
 rangeHigh = rangeLow + ( rangeStep - 1 )
 End If
 Loop While Not ( exitLoop | userFound )

IsMemberOfLargeGroup = userFound
 End Function