Tried implementing the page results control to my code to get past the 1000 users limit but it only returns as many users as I've set the page size to (e.g. page size is set to 20, it will only return 20 users). If I set the page size to over 1000, it will still max out at 1000.
Here's my current code:
public void setupStupidConnection()
{
String ldapURL = "ldap://myldapurl";
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
//set security credentials, note using simple cleartext authentication
env.put(Context.SECURITY_AUTHENTICATION,"simple");
env.put("com.sun.jndi.ldap.read.timeout", "10000");
env.put(Context.SECURITY_PRINCIPAL,"username");
env.put(Context.SECURITY_CREDENTIALS,"password");
//connect to my domain controller
env.put(Context.PROVIDER_URL,ldapURL);
}
public void stupidSearch(String searchOptions)
{
String tempString = "";
try
{
LdapContext ctx = new InitialLdapContext(env,null);
// Activate paged results
int pageSize = 20;
byte[] cookie = null;
ctx.setRequestControls(new Control[] { new PagedResultsControl(pageSize,
Control.NONCRITICAL) });
int total;
//Specify the attributes to return
String returnedAtts[]={"Name", "samAccountName", "mail", "UserAccountControl", "distinguishedName"};
searchCtls.setReturningAttributes(returnedAtts);
//Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//specify the LDAP search filter
String searchFilter;
if(!"".equals(search))
{
switch(searchOptions)
{
case "Starts with...":
searchFilter = "(&(objectClass=user) (objectCategory=person) (CN=" + search + "*))";
break;
case "Ends with...":
searchFilter = "(&(objectClass=user) (objectCategory=person) (CN=*" + search + "))";
break;
case "Contains...":
searchFilter = "(&(objectClass=user) (objectCategory=person) (CN=*" + search + "*))";
break;
case "Equals...":
searchFilter = "(&(objectClass=user) (objectCategory=person) (CN=" + search + "))";
break;
default:
searchFilter = "(&(objectClass=user) (objectCategory=person) (CN=" + search + "*))";
break;
}
}
else
{
searchFilter = "(&(objectClass=user) (objectCategory=person))";
}
//Specify the Base for the search
String searchBase;
switch(searchPath)
{
case "A":
searchBase = "OU=Accounts1";
break;
case "B":
searchBase = "OU=Accounts2";
break;
case "C":
searchBase = "OU=Accounts3";
break;
case "D":
searchBase = "OU=Accounts4";
break;
default:
searchBase = "OU=Accounts1";
break;
}
do
{
System.out.println(searchBase);
// Search for objects using the filter
NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls);
//Loop through the search results
while (answer.hasMoreElements())
{
SearchResult sr = (SearchResult)answer.next();
Attributes attrs = sr.getAttributes();
tempString = attrs.get("distinguishedName").toString();
tempString = tempString.substring(tempString.indexOf(",")+4);
tempString = tempString.substring(0, tempString.indexOf(",DC="));
String[] reverseString = tempString.split(",OU=");
List<String> reverseList = Arrays.asList(reverseString);
Collections.reverse(reverseList);
tempString = "";
for (String reverseString1: reverseString)
{
tempString = tempString + "\\" + reverseString1;
}
tempString = tempString.substring(1);
location.add(tempString);
String enableCheck = attrs.get("UserAccountControl").toString().replaceAll(".*: ", "");
String isEnabled = isActive(enableCheck).toString();
results1.add(attrs.get("Name").toString());
// System.out.println(attrs.get("Name").toString());
results2.add(attrs.get("samAccountName").toString());
results4.add(isEnabled);
if (attrs.get("mail") != null)
{
results3.add(attrs.get("mail").toString());
}
else
{
results3.add("");
}
Control[] controls = ctx.getResponseControls();
if (controls != null)
{
for (int i = 0; i < controls.length; i++)
{
if (controls[i] instanceof PagedResultsResponseControl)
{
PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i];
total = prrc.getResultSize();
if (total != 0)
{
System.out.println("***************** END-OF-PAGE "
+ "(total : " + total + ") *****************\n");
}
else
{
System.out.println("***************** END-OF-PAGE "
+ "(total: unknown) ***************\n");
}
cookie = prrc.getCookie();
}
}
}
else
{
System.out.println("No controls were sent from the server");
}
// Re-activate paged results
ctx.setRequestControls(new Control[] { new PagedResultsControl(
pageSize, cookie, Control.CRITICAL) });
}
} while (cookie != null);
ctx.close();
}
catch (NamingException e)
{
System.out.println("Search error: " + e);
System.exit(-1);
} catch (IOException ex)
{
Logger.getLogger(ADData.class.getName()).log(Level.SEVERE, null, ex);
}
Logger.getLogger(ADData.class.getName()).log(Level.SEVERE, null, ex);
}
I've changed a few things to hide some company data. For every user it returns, it always outputs "No controls were sent from the server".
Any idea of what could be causing it?
You've used Control.NONCRITICAL
. From the documentation:
criticality
- If true then the server must honor the control and return search results as indicated by pageSize or refuse to perform the search. If false, then the server need not honor the control.
So you're basically saying you're okay with it not honouring your page size. If you really, really need the page size to be honoured, used Control.CRITICAL
- but be aware that that may cause the request to fail entirely.
If you don't mind only receiving 1000 results at a time you need to use the pagination appropriately, setting the server-generated cookie to retrieve subsequent pages. It looks like you're trying to do that via the second ctx.setRequestControls
call, but your code is somewhat broken:
Your while (cookie != null)
loop isn't at the end of a do
block, so I believe you've actually got code equivalent to:
while (answer.hasMoreElements()) { ... } while (cookie != null) { ctx.close(); }
You're not performing the search again. I wouldn't personally expect setting the controls to automatically perform a new search. (I haven't used the LDAP API for ages, but I'd expect you to need to call ctx.search
for it to have an effect.)
See more on this question at Stackoverflow