Using LDAP to Enumerate Large Groups in UWWI
In your day-to-day usage of the UW Windows Infrastructure, you may notice 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 UW Windows Infrastructure, 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