Commit 52f76644 authored by Daniel Henninger's avatar Daniel Henninger Committed by dhenninger

[JM-1117] LDAP paged results are now used if the server supports it, thereby...

[JM-1117] LDAP paged results are now used if the server supports it, thereby permitting results larger than server limitations.
[JM-757] Groups now abide by altbasedn.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@10014 b35dd754-fafc-0310-a699-88a17e54d16e
parent 44ddd36e
...@@ -19,15 +19,13 @@ import org.jivesoftware.openfire.user.UserManager; ...@@ -19,15 +19,13 @@ import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException; import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.util.JiveConstants;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
import javax.naming.NamingException; import javax.naming.NamingException;
import javax.naming.directory.*; import javax.naming.directory.*;
import javax.naming.ldap.Control; import javax.naming.ldap.*;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.SortControl;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
...@@ -42,14 +40,20 @@ import java.util.regex.Pattern; ...@@ -42,14 +40,20 @@ import java.util.regex.Pattern;
public class LdapGroupProvider implements GroupProvider { public class LdapGroupProvider implements GroupProvider {
private LdapManager manager; private LdapManager manager;
private String baseDN;
private String alternateBaseDN;
private UserManager userManager; private UserManager userManager;
private String[] standardAttributes; private String[] standardAttributes;
private int groupCount = -1;
private long expiresStamp = System.currentTimeMillis();
/** /**
* Constructs a new LDAP group provider. * Constructs a new LDAP group provider.
*/ */
public LdapGroupProvider() { public LdapGroupProvider() {
manager = LdapManager.getInstance(); manager = LdapManager.getInstance();
baseDN = manager.getBaseDN();
alternateBaseDN = manager.getAlternateBaseDN();
userManager = UserManager.getInstance(); userManager = UserManager.getInstance();
standardAttributes = new String[3]; standardAttributes = new String[3];
standardAttributes[0] = manager.getGroupNameField(); standardAttributes[0] = manager.getGroupNameField();
...@@ -78,34 +82,18 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -78,34 +82,18 @@ public class LdapGroupProvider implements GroupProvider {
} }
public Group getGroup(String groupName) throws GroupNotFoundException { public Group getGroup(String groupName) throws GroupNotFoundException {
Collection<Group> groups;
LdapContext ctx = null; LdapContext ctx = null;
try { try {
ctx = manager.getContext(); String groupDN = manager.findGroupDN(groupName);
// Load record.
ctx = manager.getContext(manager.getGroupsBaseDN(groupName));
Attributes attrs = ctx.getAttributes(groupDN, standardAttributes);
// Search for the dn based on the group name. return processGroup(ctx, attrs);
SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level.
if (manager.isSubTreeSearch()) {
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
}
else {
searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
}
searchControls.setReturningAttributes(standardAttributes);
String filter = MessageFormat.format(manager.getGroupSearchFilter(), groupName);
NamingEnumeration<SearchResult> answer = ctx.search("", filter, searchControls);
groups = populateGroups(answer);
// Close the enumeration.
answer.close();
if (groups.size() == 1) {
return groups.iterator().next();
}
} }
catch (Exception e) { catch (Exception e) {
Log.error(e); Log.error(e);
throw new GroupNotFoundException(e); throw new GroupNotFoundException("Group with name " + groupName + " not found.", e);
} }
finally { finally {
try { try {
...@@ -118,12 +106,6 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -118,12 +106,6 @@ public class LdapGroupProvider implements GroupProvider {
// Ignore. // Ignore.
} }
} }
if (groups.size() > 1) {
// If multiple groups found, throw exception.
throw new GroupNotFoundException(
"Too many groups with name " + groupName + " were found.");
}
throw new GroupNotFoundException("Group with name " + groupName + " not found.");
} }
/** /**
...@@ -154,11 +136,26 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -154,11 +136,26 @@ public class LdapGroupProvider implements GroupProvider {
if (manager.isDebugEnabled()) { if (manager.isDebugEnabled()) {
Log.debug("LdapGroupProvider: Trying to get the number of groups in the system."); Log.debug("LdapGroupProvider: Trying to get the number of groups in the system.");
} }
// Cache user count for 5 minutes.
if (groupCount != -1 && System.currentTimeMillis() < expiresStamp) {
return groupCount;
}
int count = 0; int count = 0;
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
LdapContext ctx = null; LdapContext ctx = null;
LdapContext ctx2 = null;
try { try {
ctx = manager.getContext(); ctx = manager.getContext(baseDN);
// Set up request controls, if appropriate.
List<Control> tmpRequestControls = new ArrayList<Control>();
if (pageSize > -1) {
// Server side paging.
tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
}
Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx.setRequestControls(requestControls);
SearchControls searchControls = new SearchControls(); SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level. // See if recursive searching is enabled. Otherwise, only search one level.
...@@ -171,9 +168,62 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -171,9 +168,62 @@ public class LdapGroupProvider implements GroupProvider {
searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() }); searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() });
String filter = MessageFormat.format(manager.getGroupSearchFilter(), "*"); String filter = MessageFormat.format(manager.getGroupSearchFilter(), "*");
NamingEnumeration answer = ctx.search("", filter, searchControls); NamingEnumeration answer = ctx.search("", filter, searchControls);
while (answer.hasMoreElements()) { byte[] cookie = null;
answer.next();
count++; // Run through all pages of results (one page is also possible ;) )
do {
// Examine results on this page
while (answer.hasMoreElements()) {
answer.next();
count++;
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
// Add count of groups found in alternate DN
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
answer = ctx2.search("", filter, searchControls);
cookie = null;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine results on this page
while (answer.hasMoreElements()) {
count++;
answer.nextElement();
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
} }
// Close the enumeration. // Close the enumeration.
answer.close(); answer.close();
...@@ -187,78 +237,45 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -187,78 +237,45 @@ public class LdapGroupProvider implements GroupProvider {
ctx.setRequestControls(null); ctx.setRequestControls(null);
ctx.close(); ctx.close();
} }
if (ctx2 != null) {
ctx2.setRequestControls(null);
ctx2.close();
}
} }
catch (Exception ignored) { catch (Exception ignored) {
// Ignore. // Ignore.
} }
} }
this.groupCount = count;
this.expiresStamp = System.currentTimeMillis() + JiveConstants.MINUTE *5;
return count; return count;
} }
public Collection<String> getGroupNames() { public Collection<String> getGroupNames() {
List<String> groupNames = new ArrayList<String>(); return getGroupNames(-1, -1);
LdapContext ctx = null;
try {
ctx = manager.getContext();
// Sort on group name field.
Control[] searchControl = new Control[]{
new SortControl(new String[]{manager.getGroupNameField()}, Control.NONCRITICAL)
};
ctx.setRequestControls(searchControl);
SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level.
if (manager.isSubTreeSearch()) {
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
}
else {
searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
}
searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() });
String filter = MessageFormat.format(manager.getGroupSearchFilter(), "*");
NamingEnumeration answer = ctx.search("", filter, searchControls);
while (answer.hasMoreElements()) {
// Get the next group.
String groupName = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getGroupNameField()).get();
// Escape group name and add to results.
groupNames.add(JID.escapeNode(groupName));
}
// Close the enumeration.
answer.close();
// If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) {
Collections.sort(groupNames);
}
}
catch (Exception e) {
Log.error(e);
}
finally {
try {
if (ctx != null) {
ctx.setRequestControls(null);
ctx.close();
}
}
catch (Exception ignored) {
// Ignore.
}
}
return groupNames;
} }
public Collection<String> getGroupNames(int startIndex, int numResults) { public Collection<String> getGroupNames(int startIndex, int numResults) {
List<String> groupNames = new ArrayList<String>(); List<String> groupNames = new ArrayList<String>();
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
LdapContext ctx = null; LdapContext ctx = null;
LdapContext ctx2 = null;
try { try {
ctx = manager.getContext(); ctx = manager.getContext(baseDN);
// Sort on group name field.
Control[] searchControl = new Control[]{ // Set up request controls, if appropriate.
new SortControl(new String[]{manager.getGroupNameField()}, Control.NONCRITICAL) List<Control> tmpRequestControls = new ArrayList<Control>();
}; if (!clientSideSort) {
ctx.setRequestControls(searchControl); // Server side sort on username field.
tmpRequestControls.add(new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL));
}
if (pageSize > -1) {
// Server side paging.
tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
}
Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx.setRequestControls(requestControls);
SearchControls searchControls = new SearchControls(); SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level. // See if recursive searching is enabled. Otherwise, only search one level.
...@@ -270,34 +287,102 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -270,34 +287,102 @@ public class LdapGroupProvider implements GroupProvider {
} }
searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() }); searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() });
String filter = MessageFormat.format(manager.getGroupSearchFilter(), "*"); String filter = MessageFormat.format(manager.getGroupSearchFilter(), "*");
// TODO: used paged results if supported by LDAP server.
NamingEnumeration answer = ctx.search("", filter, searchControls); NamingEnumeration answer = ctx.search("", filter, searchControls);
for (int i=0; i < startIndex; i++) { // If server side sort, we'll skip the initial ones we don't want, and stop when we've hit
if (answer.hasMoreElements()) { // the amount we do want.
answer.next(); int skip = -1;
} int lastRes = -1;
else { if (!clientSideSort) {
return Collections.emptyList(); skip = startIndex;
} lastRes = startIndex + numResults;
}
// Now read in desired number of results (or stop if we run out of results). }
for (int i = 0; i < numResults; i++) { byte[] cookie = null;
if (answer.hasMoreElements()) { int count = 0;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all of the results on this page
while (answer.hasMoreElements()) {
count++;
if (skip > -1 && count < skip) {
continue;
}
if (lastRes > -1 && count > lastRes) {
break;
}
// Get the next group. // Get the next group.
String groupName = (String)((SearchResult)answer.next()).getAttributes().get( String groupName = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getGroupNameField()).get(); manager.getGroupNameField()).get();
// Escape group name and add to results. // Escape group name and add to results.
groupNames.add(JID.escapeNode(groupName)); groupNames.add(JID.escapeNode(groupName));
} }
else { // Examine the paged results control response
break; Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
} }
} while (cookie != null);
// Add groups found in alternate DN
if (count <= lastRes && alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(requestControls);
answer = ctx2.search("", filter, searchControls);
cookie = null;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all of the results on this page
while (answer.hasMoreElements()) {
count++;
if (skip > -1 && count < skip) {
continue;
}
if (lastRes > -1 && count > lastRes) {
break;
}
// Get the next group.
String groupName = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getGroupNameField()).get();
// Escape group name and add to results.
groupNames.add(JID.escapeNode(groupName));
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
} }
// Close the enumeration. // Close the enumeration.
answer.close(); answer.close();
// If client-side sorting is enabled, sort. // If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) { if (clientSideSort) {
Collections.sort(groupNames); Collections.sort(groupNames);
} }
} }
...@@ -310,6 +395,10 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -310,6 +395,10 @@ public class LdapGroupProvider implements GroupProvider {
ctx.setRequestControls(null); ctx.setRequestControls(null);
ctx.close(); ctx.close();
} }
if (ctx2 != null) {
ctx2.setRequestControls(null);
ctx2.close();
}
} }
catch (Exception ignored) { catch (Exception ignored) {
// Ignore. // Ignore.
...@@ -344,10 +433,27 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -344,10 +433,27 @@ public class LdapGroupProvider implements GroupProvider {
return Collections.emptyList(); return Collections.emptyList();
} }
// Perform the LDAP query // Perform the LDAP query
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
List<String> groupNames = new ArrayList<String>(); List<String> groupNames = new ArrayList<String>();
LdapContext ctx = null; LdapContext ctx = null;
LdapContext ctx2 = null;
try { try {
ctx = manager.getContext(); ctx = manager.getContext(baseDN);
// Set up request controls, if appropriate.
List<Control> tmpRequestControls = new ArrayList<Control>();
if (!clientSideSort) {
// Server side sort on username field.
tmpRequestControls.add(new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL));
}
if (pageSize > -1) {
// Server side paging.
tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
}
Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx.setRequestControls(requestControls);
// Search for the dn based on the group name. // Search for the dn based on the group name.
SearchControls searchControls = new SearchControls(); SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level. // See if recursive searching is enabled. Otherwise, only search one level.
...@@ -368,17 +474,71 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -368,17 +474,71 @@ public class LdapGroupProvider implements GroupProvider {
Log.debug("Trying to find group names for user: " + user + " using query: " + filter.toString()); Log.debug("Trying to find group names for user: " + user + " using query: " + filter.toString());
} }
NamingEnumeration answer = ctx.search("", filter.toString(), searchControls); NamingEnumeration answer = ctx.search("", filter.toString(), searchControls);
while (answer.hasMoreElements()) { byte[] cookie = null;
// Get the next group. // Run through all pages of results (one page is also possible ;) )
String groupName = (String)((SearchResult)answer.next()).getAttributes().get( do {
manager.getGroupNameField()).get(); // Examine all results on this page
// Escape group name and add to results. while (answer.hasMoreElements()) {
groupNames.add(JID.escapeNode(groupName)); // Get the next group.
String groupName = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getGroupNameField()).get();
// Escape group name and add to results.
groupNames.add(JID.escapeNode(groupName));
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(requestControls);
answer = ctx2.search("", filter.toString(), searchControls);
cookie = null;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all results on this page
while (answer.hasMoreElements()) {
// Get the next group.
String groupName = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getGroupNameField()).get();
// Escape group name and add to results.
groupNames.add(JID.escapeNode(groupName));
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
} }
// Close the enumeration. // Close the enumeration.
answer.close(); answer.close();
// If client-side sorting is enabled, sort. // If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) { if (clientSideSort) {
Collections.sort(groupNames); Collections.sort(groupNames);
} }
} }
...@@ -392,6 +552,10 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -392,6 +552,10 @@ public class LdapGroupProvider implements GroupProvider {
ctx.setRequestControls(null); ctx.setRequestControls(null);
ctx.close(); ctx.close();
} }
if (ctx2 != null) {
ctx2.setRequestControls(null);
ctx2.close();
}
} }
catch (Exception ignored) { catch (Exception ignored) {
// Ignore. // Ignore.
...@@ -448,66 +612,7 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -448,66 +612,7 @@ public class LdapGroupProvider implements GroupProvider {
} }
public Collection<String> search(String query) { public Collection<String> search(String query) {
if (query == null || "".equals(query)) { return search(query, -1, -1);
return Collections.emptyList();
}
// Make the query be a wildcard search by default. So, if the user searches for
// "Test", make the search be "Test*" instead.
if (!query.endsWith("*")) {
query = query + "*";
}
List<String> groupNames = new ArrayList<String>();
LdapContext ctx = null;
try {
ctx = manager.getContext();
// Sort on username field.
Control[] searchControl = new Control[]{
new SortControl(new String[]{manager.getGroupNameField()}, Control.NONCRITICAL)
};
ctx.setRequestControls(searchControl);
// Search for the dn based on the group name.
SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level.
if (manager.isSubTreeSearch()) {
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
}
else {
searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
}
searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() });
StringBuilder filter = new StringBuilder();
filter.append("(").append(manager.getGroupNameField()).append("=").append(query).append(")");
NamingEnumeration answer = ctx.search("", filter.toString(), searchControls);
while (answer.hasMoreElements()) {
// Get the next group.
String groupName = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getGroupNameField()).get();
// Escape group name and add to results.
groupNames.add(JID.escapeNode(groupName));
}
// Close the enumeration.
answer.close();
// If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) {
Collections.sort(groupNames);
}
}
catch (Exception e) {
Log.error(e);
}
finally {
try {
if (ctx != null) {
ctx.setRequestControls(null);
ctx.close();
}
}
catch (Exception ignored) {
// Ignore.
}
}
return groupNames;
} }
public Collection<String> search(String query, int startIndex, int numResults) { public Collection<String> search(String query, int startIndex, int numResults) {
...@@ -520,14 +625,25 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -520,14 +625,25 @@ public class LdapGroupProvider implements GroupProvider {
query = query + "*"; query = query + "*";
} }
List<String> groupNames = new ArrayList<String>(); List<String> groupNames = new ArrayList<String>();
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
LdapContext ctx = null; LdapContext ctx = null;
LdapContext ctx2 = null;
try { try {
ctx = manager.getContext(); ctx = manager.getContext(baseDN);
// Sort on username field.
Control[] searchControl = new Control[]{ // Set up request controls, if appropriate.
new SortControl(new String[]{manager.getGroupNameField()}, Control.NONCRITICAL) List<Control> tmpRequestControls = new ArrayList<Control>();
}; if (!clientSideSort) {
ctx.setRequestControls(searchControl); // Server side sort on username field.
tmpRequestControls.add(new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL));
}
if (pageSize > -1) {
// Server side paging.
tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
}
Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx.setRequestControls(requestControls);
// Search for the dn based on the group name. // Search for the dn based on the group name.
SearchControls searchControls = new SearchControls(); SearchControls searchControls = new SearchControls();
...@@ -541,34 +657,98 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -541,34 +657,98 @@ public class LdapGroupProvider implements GroupProvider {
searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() }); searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() });
StringBuilder filter = new StringBuilder(); StringBuilder filter = new StringBuilder();
filter.append("(").append(manager.getGroupNameField()).append("=").append(query).append(")"); filter.append("(").append(manager.getGroupNameField()).append("=").append(query).append(")");
// TODO: used paged results if supported by LDAP server.
NamingEnumeration answer = ctx.search("", filter.toString(), searchControls); NamingEnumeration answer = ctx.search("", filter.toString(), searchControls);
for (int i=0; i < startIndex; i++) { // If server side sort, we'll skip the initial ones we don't want, and stop when we've hit
if (answer.hasMoreElements()) { // the amount we do want.
answer.next(); int skip = -1;
} int lastRes = -1;
else { if (!clientSideSort) {
return Collections.emptyList(); skip = startIndex;
} lastRes = startIndex + numResults;
}
// Now read in desired number of results (or stop if we run out of results). }
for (int i = 0; i < numResults; i++) { byte[] cookie = null;
if (answer.hasMoreElements()) { int count = 0;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all results on this page
while (answer.hasMoreElements()) {
count++;
if (skip > -1 && count < skip) {
continue;
}
if (lastRes > -1 && count > lastRes) {
break;
}
// Get the next group. // Get the next group.
String groupName = (String)((SearchResult)answer.next()).getAttributes().get( String groupName = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getGroupNameField()).get(); manager.getGroupNameField()).get();
// Escape group name and add to results. // Escape group name and add to results.
groupNames.add(JID.escapeNode(groupName)); groupNames.add(JID.escapeNode(groupName));
} }
else { // Examine the paged results control response
break; Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
} }
} while (cookie != null);
if (count <= lastRes && alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(requestControls);
answer = ctx2.search("", filter.toString(), searchControls);
cookie = null;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all results on this page
while (answer.hasMoreElements()) {
count++;
if (skip > -1 && count < skip) {
continue;
}
if (lastRes > -1 && count > lastRes) {
break;
}
// Get the next group.
String groupName = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getGroupNameField()).get();
// Escape group name and add to results.
groupNames.add(JID.escapeNode(groupName));
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
} }
// Close the enumeration. // Close the enumeration.
answer.close(); answer.close();
// If client-side sorting is enabled, sort. // If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) { if (clientSideSort) {
Collections.sort(groupNames); Collections.sort(groupNames);
} }
} }
...@@ -581,6 +761,10 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -581,6 +761,10 @@ public class LdapGroupProvider implements GroupProvider {
ctx.setRequestControls(null); ctx.setRequestControls(null);
ctx.close(); ctx.close();
} }
if (ctx2 != null) {
ctx2.setRequestControls(null);
ctx2.close();
}
} }
catch (Exception ignored) { catch (Exception ignored) {
// Ignore. // Ignore.
...@@ -598,143 +782,96 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -598,143 +782,96 @@ public class LdapGroupProvider implements GroupProvider {
* *
* @param answer LDAP search result. * @param answer LDAP search result.
* @return a collection of groups. * @return a collection of groups.
* @throws javax.naming.NamingException * @throws javax.naming.NamingException if there was an exception with the LDAP query.
*/ */
private Collection<Group> populateGroups(Enumeration<SearchResult> answer) throws NamingException { private Collection<Group> populateGroups(Enumeration<SearchResult> answer) throws NamingException {
if (manager.isDebugEnabled()) { if (manager.isDebugEnabled()) {
Log.debug("LdapGroupProvider: Starting to populate groups with users."); Log.debug("LdapGroupProvider: Starting to populate groups with users.");
} }
DirContext ctx = null; int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
LdapContext ctx = null;
LdapContext ctx2 = null;
try { try {
TreeMap<String, Group> groups = new TreeMap<String, Group>(); TreeMap<String, Group> groups = new TreeMap<String, Group>();
ctx = manager.getContext(); ctx = manager.getContext(baseDN);
SearchControls searchControls = new SearchControls(); // Set up request controls, if appropriate.
searchControls.setReturningAttributes(new String[] { manager.getUsernameField() }); List<Control> tmpRequestControls = new ArrayList<Control>();
// See if recursive searching is enabled. Otherwise, only search one level. if (pageSize > -1) {
if (manager.isSubTreeSearch()) { // Server side paging.
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
}
else {
searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
} }
Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx.setRequestControls(requestControls);
byte[] cookie = null;
XMPPServer server = XMPPServer.getInstance(); // Run through all pages of results (one page is also possible ;) )
String serverName = server.getServerInfo().getXMPPDomain(); do {
// Build 3 groups. // Examine all results on this page
// group 1: uid= while (answer.hasMoreElements()) {
// group 2: rest of the text until first comma String name = "";
// group 3: rest of the text
Pattern pattern =
Pattern.compile("(?i)(^" + manager.getUsernameField() + "=)([^,]+)(.+)");
while (answer.hasMoreElements()) {
String name = "";
try {
Attributes a = answer.nextElement().getAttributes();
String description;
try { try {
name = ((String)((a.get(manager.getGroupNameField())).get())); Group group = processGroup(ctx, answer.nextElement().getAttributes());
description = name = group.getName();
((String)((a.get(manager.getGroupDescriptionField())).get())); groups.put(name, group);
} }
catch (Exception e) { catch (Exception e) {
description = ""; Log.error("LdapGroupProvider: Error while populating group, " + name + ".", e);
} }
Set<JID> members = new TreeSet<JID>(); }
Attribute memberField = a.get(manager.getGroupMemberField()); // Examine the paged results control response
if (memberField != null) { Control[] controls = ctx.getResponseControls();
NamingEnumeration ne = memberField.getAll(); if (controls != null) {
while (ne.hasMore()) { for (Control control : controls) {
String username = (String) ne.next(); if (control instanceof PagedResultsResponseControl) {
// If not posix mode, each group member is stored as a full DN. PagedResultsResponseControl prrc =
if (!manager.isPosixMode()) { (PagedResultsResponseControl) control;
try { cookie = prrc.getCookie();
// Try to find the username with a regex pattern match.
Matcher matcher = pattern.matcher(username); // Re-activate paged results
if (matcher.matches() && matcher.groupCount() == 3) { ctx.setRequestControls(new Control[]{
// The username is in the DN, no additional search needed new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
username = matcher.group(2); break;
}
// The regex pattern match failed. This will happen if the
// the member DN's don't use the standard username field. For
// example, Active Directory has a username field of
// sAMAccountName, but stores group members as "CN=...".
else {
// Create an LDAP name with the full DN.
LdapName ldapName = new LdapName(username);
// Turn the LDAP name into something we can use in a
// search by stripping off the comma.
String userDNPart = ldapName.get(ldapName.size() - 1);
NamingEnumeration usrAnswer = ctx.search("",
userDNPart, searchControls);
if (usrAnswer != null && usrAnswer.hasMoreElements()) {
username = (String) ((SearchResult) usrAnswer.next())
.getAttributes().get(
manager.getUsernameField()).get();
}
// Close the enumeration.
usrAnswer.close();
}
}
catch (Exception e) {
Log.error(e);
}
}
// A search filter may have been defined in the LdapUserProvider.
// Therefore, we have to try to load each user we found to see if
// it passes the filter.
try {
JID userJID;
int position = username.indexOf("@" + serverName);
// Create JID of local user if JID does not match a component's JID
if (position == -1) {
// In order to lookup a username from the manager, the username
// must be a properly escaped JID node.
String escapedUsername = JID.escapeNode(username);
if (!escapedUsername.equals(username)) {
// Check if escaped username is valid
userManager.getUser(escapedUsername);
}
// No exception, so the user must exist. Add the user as a group
// member using the escaped username.
userJID = server.createJID(escapedUsername, null);
}
else {
// This is a JID of a component or node of a server's component
String node = username.substring(0, position);
String escapedUsername = JID.escapeNode(node);
userJID = new JID(escapedUsername + "@" + serverName);
}
members.add(userJID);
}
catch (UserNotFoundException e) {
// We can safely ignore this error. It likely means that
// the user didn't pass the search filter that's defined.
// So, we want to simply ignore the user as a group member.
if (manager.isDebugEnabled()) {
Log.debug("LdapGroupProvider: User not found: " + username);
}
}
} }
// Close the enumeration.
ne.close();
} }
if (manager.isDebugEnabled()) {
Log.debug("LdapGroupProvider: Adding group \"" + name + "\" with " + members.size() +
" members.");
}
Collection<JID> admins = Collections.emptyList();
Group group = new Group(name, description, members, admins);
groups.put(name, group);
} }
catch (Exception e) { } while (cookie != null);
e.printStackTrace(); if (alternateBaseDN != null) {
if (manager.isDebugEnabled()) { ctx2 = manager.getContext(alternateBaseDN);
Log.debug("LdapGroupProvider: Error while populating group, " + name + ".", e); ctx2.setRequestControls(requestControls);
cookie = null;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all results on this page
while (answer.hasMoreElements()) {
String name = "";
try {
Group group = processGroup(ctx, answer.nextElement().getAttributes());
name = group.getName();
groups.put(name, group);
}
catch (Exception e) {
Log.error("LdapGroupProvider: Error while populating group, " + name + ".", e);
}
} }
} // Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
} }
if (manager.isDebugEnabled()) { if (manager.isDebugEnabled()) {
Log.debug("LdapGroupProvider: Finished populating group(s) with users."); Log.debug("LdapGroupProvider: Finished populating group(s) with users.");
...@@ -742,15 +879,143 @@ public class LdapGroupProvider implements GroupProvider { ...@@ -742,15 +879,143 @@ public class LdapGroupProvider implements GroupProvider {
return groups.values(); return groups.values();
} }
catch (Exception e) {
Log.error("Exception while attempting to populate groups and members: ", e);
return new ArrayList<Group>();
}
finally { finally {
try { try {
if (ctx != null) { if (ctx != null) {
ctx.close(); ctx.close();
} }
if (ctx2 != null) {
ctx2.close();
}
} }
catch (Exception e) { catch (Exception e) {
// Ignore. // Ignore.
} }
} }
} }
private Group processGroup(LdapContext ctx, Attributes a) throws NamingException {
XMPPServer server = XMPPServer.getInstance();
String serverName = server.getServerInfo().getXMPPDomain();
// Build 3 groups.
// group 1: uid=
// group 2: rest of the text until first comma
// group 3: rest of the text
Pattern pattern =
Pattern.compile("(?i)(^" + manager.getUsernameField() + "=)([^,]+)(.+)");
SearchControls searchControls = new SearchControls();
searchControls.setReturningAttributes(new String[] { manager.getUsernameField() });
// See if recursive searching is enabled. Otherwise, only search one level.
if (manager.isSubTreeSearch()) {
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
}
else {
searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
}
String name;
String description;
try {
name = ((String)((a.get(manager.getGroupNameField())).get()));
}
catch (Exception e) {
name = "";
}
try {
description = ((String)((a.get(manager.getGroupDescriptionField())).get()));
}
catch (Exception e) {
description = "";
}
Set<JID> members = new TreeSet<JID>();
Attribute memberField = a.get(manager.getGroupMemberField());
if (memberField != null) {
NamingEnumeration ne = memberField.getAll();
while (ne.hasMore()) {
String username = (String) ne.next();
// If not posix mode, each group member is stored as a full DN.
if (!manager.isPosixMode()) {
try {
// Try to find the username with a regex pattern match.
Matcher matcher = pattern.matcher(username);
if (matcher.matches() && matcher.groupCount() == 3) {
// The username is in the DN, no additional search needed
username = matcher.group(2);
}
// The regex pattern match failed. This will happen if the
// the member DN's don't use the standard username field. For
// example, Active Directory has a username field of
// sAMAccountName, but stores group members as "CN=...".
else {
// Create an LDAP name with the full DN.
LdapName ldapName = new LdapName(username);
// Turn the LDAP name into something we can use in a
// search by stripping off the comma.
String userDNPart = ldapName.get(ldapName.size() - 1);
NamingEnumeration usrAnswer = ctx.search("",
userDNPart, searchControls);
if (usrAnswer != null && usrAnswer.hasMoreElements()) {
username = (String) ((SearchResult) usrAnswer.next())
.getAttributes().get(
manager.getUsernameField()).get();
}
// Close the enumeration.
usrAnswer.close();
}
}
catch (Exception e) {
Log.error(e);
}
}
// A search filter may have been defined in the LdapUserProvider.
// Therefore, we have to try to load each user we found to see if
// it passes the filter.
try {
JID userJID;
int position = username.indexOf("@" + serverName);
// Create JID of local user if JID does not match a component's JID
if (position == -1) {
// In order to lookup a username from the manager, the username
// must be a properly escaped JID node.
String escapedUsername = JID.escapeNode(username);
if (!escapedUsername.equals(username)) {
// Check if escaped username is valid
userManager.getUser(escapedUsername);
}
// No exception, so the user must exist. Add the user as a group
// member using the escaped username.
userJID = server.createJID(escapedUsername, null);
}
else {
// This is a JID of a component or node of a server's component
String node = username.substring(0, position);
String escapedUsername = JID.escapeNode(node);
userJID = new JID(escapedUsername + "@" + serverName);
}
members.add(userJID);
}
catch (UserNotFoundException e) {
// We can safely ignore this error. It likely means that
// the user didn't pass the search filter that's defined.
// So, we want to simply ignore the user as a group member.
if (manager.isDebugEnabled()) {
Log.debug("LdapGroupProvider: User not found: " + username);
}
}
}
// Close the enumeration.
ne.close();
}
if (manager.isDebugEnabled()) {
Log.debug("LdapGroupProvider: Adding group \"" + name + "\" with " + members.size() +
" members.");
}
Collection<JID> admins = Collections.emptyList();
return new Group(name, description, members, admins);
}
} }
\ No newline at end of file
...@@ -14,6 +14,7 @@ package org.jivesoftware.openfire.ldap; ...@@ -14,6 +14,7 @@ package org.jivesoftware.openfire.ldap;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.openfire.user.UserNotFoundException; import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import javax.naming.Context; import javax.naming.Context;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
...@@ -28,6 +29,7 @@ import java.net.URLEncoder; ...@@ -28,6 +29,7 @@ import java.net.URLEncoder;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.text.MessageFormat;
/** /**
* Centralized administration of LDAP connections. The {@link #getInstance()} method * Centralized administration of LDAP connections. The {@link #getInstance()} method
...@@ -146,6 +148,7 @@ public class LdapManager { ...@@ -146,6 +148,7 @@ public class LdapManager {
private String searchFilter = null; private String searchFilter = null;
private boolean subTreeSearch; private boolean subTreeSearch;
private boolean encloseUserDN; private boolean encloseUserDN;
private boolean encloseGroupDN;
private String groupNameField; private String groupNameField;
private String groupMemberField; private String groupMemberField;
...@@ -154,6 +157,7 @@ public class LdapManager { ...@@ -154,6 +157,7 @@ public class LdapManager {
private String groupSearchFilter = null; private String groupSearchFilter = null;
private Pattern userDNPattern; private Pattern userDNPattern;
private Pattern groupDNPattern;
private Map<String, String> properties; private Map<String, String> properties;
...@@ -282,8 +286,15 @@ public class LdapManager { ...@@ -282,8 +286,15 @@ public class LdapManager {
if (encloseUserStr != null) { if (encloseUserStr != null) {
encloseUserDN = Boolean.valueOf(encloseUserStr); encloseUserDN = Boolean.valueOf(encloseUserStr);
} }
encloseGroupDN = true;
String encloseGroupStr = properties.get("ldap.encloseGroupDN");
if (encloseGroupStr != null) {
encloseGroupDN = Boolean.valueOf(encloseGroupStr);
}
// Set the pattern to use to wrap userDNs values " // Set the pattern to use to wrap userDNs values "
userDNPattern = Pattern.compile("(=)([^\\\"][^=]*[^\\\"])(?:,|$)"); userDNPattern = Pattern.compile("(=)([^\\\"][^=]*[^\\\"])(?:,|$)");
// Set the pattern to use to wrap groupDNs values "
groupDNPattern = Pattern.compile("(=)([^\\\"][^=]*[^\\\"])(?:,|$)");
this.initialContextFactory = properties.get("ldap.initialContextFactory"); this.initialContextFactory = properties.get("ldap.initialContextFactory");
if (initialContextFactory != null) { if (initialContextFactory != null) {
try { try {
...@@ -683,6 +694,156 @@ public class LdapManager { ...@@ -683,6 +694,156 @@ public class LdapManager {
} }
} }
/**
* Finds a groups's dn using it's group name. Normally, this search will
* be performed using the field "cn", but this can be changed by setting
* the <tt>groupNameField</tt> property.<p>
*
* Searches are performed over all subtrees relative to the <tt>baseDN</tt>.
* If the search fails in the <tt>baseDN</tt> then another search will be
* performed in the <tt>alternateBaseDN</tt>. For example, if the <tt>baseDN</tt>
* is "o=jivesoftware, o=com" and we do a search for "managers", then we might
* find a groupDN of "uid=managers,ou=Groups". This kind of searching is a good
* thing since it doesn't make the assumption that all user records are stored
* in a flat structure. However, it does add the requirement that "cn" field
* (or the other field specified) must be unique over the entire subtree from
* the <tt>baseDN</tt>. For example, it's entirely possible to create two dn's
* in your LDAP directory with the same cn: "cn=managers,ou=Financial" and
* "cn=managers,ou=Engineers". In such a case, it's not possible to
* uniquely identify a group, so this method will throw an error.<p>
*
* The dn that's returned is relative to the default <tt>baseDN</tt>.
*
* @param groupname the groupname to lookup the dn for.
* @return the dn associated with <tt>groupname</tt>.
* @throws Exception if the search for the dn fails.
*/
public String findGroupDN(String groupname) throws Exception {
try {
return findGroupDN(groupname, baseDN);
}
catch (Exception e) {
if (alternateBaseDN != null) {
return findGroupDN(groupname, alternateBaseDN);
}
else {
throw e;
}
}
}
/**
* Finds a groups's dn using it's group name. Normally, this search will
* be performed using the field "cn", but this can be changed by setting
* the <tt>groupNameField</tt> property.<p>
*
* Searches are performed over all subtrees relative to the <tt>baseDN</tt>.
* If the search fails in the <tt>baseDN</tt> then another search will be
* performed in the <tt>alternateBaseDN</tt>. For example, if the <tt>baseDN</tt>
* is "o=jivesoftware, o=com" and we do a search for "managers", then we might
* find a groupDN of "uid=managers,ou=Groups". This kind of searching is a good
* thing since it doesn't make the assumption that all user records are stored
* in a flat structure. However, it does add the requirement that "cn" field
* (or the other field specified) must be unique over the entire subtree from
* the <tt>baseDN</tt>. For example, it's entirely possible to create two dn's
* in your LDAP directory with the same cn: "cn=managers,ou=Financial" and
* "cn=managers,ou=Engineers". In such a case, it's not possible to
* uniquely identify a group, so this method will throw an error.<p>
*
* The dn that's returned is relative to the default <tt>baseDN</tt>.
*
* @param groupname the groupname to lookup the dn for.
* @param baseDN the base DN to use for this search.
* @return the dn associated with <tt>groupname</tt>.
* @throws Exception if the search for the dn fails.
* @see #findGroupDN(String) to search using the default baseDN and alternateBaseDN.
*/
public String findGroupDN(String groupname, String baseDN) throws Exception {
boolean debug = Log.isDebugEnabled();
if (debug) {
Log.debug("LdapManager: Trying to find a groups's DN based on it's groupname. " + groupNameField + ": " + groupname
+ ", Base DN: " + baseDN + "...");
}
DirContext ctx = null;
try {
ctx = getContext(baseDN);
if (debug) {
Log.debug("LdapManager: Starting LDAP search...");
}
// Search for the dn based on the groupname.
SearchControls constraints = new SearchControls();
// If sub-tree searching is enabled (default is true) then search the entire tree.
if (subTreeSearch) {
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
}
// Otherwise, only search a single level.
else {
constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE);
}
constraints.setReturningAttributes(new String[] { groupNameField });
String filter = MessageFormat.format(getGroupSearchFilter(), groupname);
NamingEnumeration answer = ctx.search("", filter, constraints);
if (debug) {
Log.debug("LdapManager: ... search finished");
}
if (answer == null || !answer.hasMoreElements()) {
if (debug) {
Log.debug("LdapManager: Group DN based on groupname '" + groupname + "' not found.");
}
throw new GroupNotFoundException("Groupname " + groupname + " not found");
}
String groupDN = ((SearchResult)answer.next()).getName();
// Make sure there are no more search results. If there are, then
// the groupname isn't unique on the LDAP server (a perfectly possible
// scenario since only fully qualified dn's need to be unqiue).
// There really isn't a way to handle this, so throw an exception.
// The baseDN must be set correctly so that this doesn't happen.
if (answer.hasMoreElements()) {
if (debug) {
Log.debug("LdapManager: Search for groupDN based on groupname '" + groupname + "' found multiple " +
"responses, throwing exception.");
}
throw new GroupNotFoundException("LDAP groupname lookup for " + groupname +
" matched multiple entries.");
}
// Close the enumeration.
answer.close();
// All other methods assume that groupDN is not a full LDAP string.
// However if a referal was followed this is not the case. The
// following code converts a referral back to a "partial" LDAP string.
if (groupDN.startsWith("ldap://")) {
groupDN = groupDN.replace("," + baseDN, "");
groupDN = groupDN.substring(groupDN.lastIndexOf("/") + 1);
groupDN = java.net.URLDecoder.decode(groupDN, "UTF-8");
}
if (encloseGroupDN) {
// Enclose groupDN values between "
// eg. cn=Domain\, Users,ou=Administrators --> cn="Domain\, Users",ou="Administrators"
Matcher matcher = groupDNPattern.matcher(groupDN);
groupDN = matcher.replaceAll("$1\"$2\",");
if (groupDN.endsWith(",")) {
groupDN = groupDN.substring(0, groupDN.length() - 1);
}
}
return groupDN;
}
catch (Exception e) {
if (debug) {
Log.debug("LdapManager: Exception thrown when searching for groupDN based on groupname '" + groupname + "'", e);
}
throw e;
}
finally {
try { ctx.close(); }
catch (Exception ignored) {
// Ignore.
}
}
}
/** /**
* Returns a properly encoded URL for use as the PROVIDER_URL. * Returns a properly encoded URL for use as the PROVIDER_URL.
* If the encoding fails then the URL will contain the raw base dn. * If the encoding fails then the URL will contain the raw base dn.
...@@ -991,6 +1152,32 @@ public class LdapManager { ...@@ -991,6 +1152,32 @@ public class LdapManager {
return null; return null;
} }
/**
* Returns the BaseDN for the given groupname.
*
* @param groupname groupname to return its base DN.
* @return the BaseDN for the given groupname. If no baseDN is found,
* this method will return <tt>null</tt>.
*/
public String getGroupsBaseDN(String groupname) {
try {
findGroupDN(groupname, baseDN);
return baseDN;
}
catch (Exception e) {
try {
if (alternateBaseDN != null) {
findGroupDN(groupname, alternateBaseDN);
return alternateBaseDN;
}
}
catch (Exception ex) {
Log.debug(ex);
}
}
return null;
}
/** /**
* Returns the starting admin DN that searches for admins will performed with. * Returns the starting admin DN that searches for admins will performed with.
* Searches will performed on the entire sub-tree under the admin DN. * Searches will performed on the entire sub-tree under the admin DN.
......
...@@ -20,9 +20,7 @@ import org.xmpp.packet.JID; ...@@ -20,9 +20,7 @@ import org.xmpp.packet.JID;
import javax.naming.NamingEnumeration; import javax.naming.NamingEnumeration;
import javax.naming.directory.*; import javax.naming.directory.*;
import javax.naming.ldap.Control; import javax.naming.ldap.*;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.SortControl;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
...@@ -144,10 +142,21 @@ public class LdapUserProvider implements UserProvider { ...@@ -144,10 +142,21 @@ public class LdapUserProvider implements UserProvider {
return userCount; return userCount;
} }
int count = 0; int count = 0;
DirContext ctx = null; int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
DirContext ctx2 = null; LdapContext ctx = null;
LdapContext ctx2 = null;
try { try {
ctx = manager.getContext(baseDN); ctx = manager.getContext(baseDN);
// Set up request controls, if appropriate.
List<Control> tmpRequestControls = new ArrayList<Control>();
if (pageSize > -1) {
// Server side paging.
tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
}
Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx.setRequestControls(requestControls);
// Search for the dn based on the username. // Search for the dn based on the username.
SearchControls searchControls = new SearchControls(); SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level. // See if recursive searching is enabled. Otherwise, only search one level.
...@@ -160,18 +169,62 @@ public class LdapUserProvider implements UserProvider { ...@@ -160,18 +169,62 @@ public class LdapUserProvider implements UserProvider {
searchControls.setReturningAttributes(new String[] { manager.getUsernameField() }); searchControls.setReturningAttributes(new String[] { manager.getUsernameField() });
String filter = MessageFormat.format(manager.getSearchFilter(), "*"); String filter = MessageFormat.format(manager.getSearchFilter(), "*");
NamingEnumeration answer = ctx.search("", filter, searchControls); NamingEnumeration answer = ctx.search("", filter, searchControls);
while (answer.hasMoreElements()) { byte[] cookie = null;
count++;
answer.nextElement(); // Run through all pages of results (one page is also possible ;) )
} do {
// Add count of users found in alternate DN // Examine results on this page
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
answer = ctx2.search("", filter, searchControls);
while (answer.hasMoreElements()) { while (answer.hasMoreElements()) {
count++; count++;
answer.nextElement(); answer.nextElement();
} }
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
// Add count of users found in alternate DN
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
answer = ctx2.search("", filter, searchControls);
cookie = null;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine results on this page
while (answer.hasMoreElements()) {
count++;
answer.nextElement();
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
} }
// Close the enumeration. // Close the enumeration.
answer.close(); answer.close();
...@@ -202,23 +255,27 @@ public class LdapUserProvider implements UserProvider { ...@@ -202,23 +255,27 @@ public class LdapUserProvider implements UserProvider {
return count; return count;
} }
public Collection<User> getUsers() {
Collection<String> usernames = getUsernames();
return new UserCollection(usernames.toArray(new String[usernames.size()]));
}
public Collection<String> getUsernames() { public Collection<String> getUsernames() {
Set<String> usernames = new HashSet<String>(); Set<String> usernames = new HashSet<String>();
LdapContext ctx = null; LdapContext ctx = null;
LdapContext ctx2 = null; LdapContext ctx2 = null;
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
try { try {
ctx = manager.getContext(baseDN); ctx = manager.getContext(baseDN);
// Sort on username field. // Set up request controls, if appropriate.
Control[] searchControl = new Control[]{ List<Control> tmpRequestControls = new ArrayList<Control>();
new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL) if (!clientSideSort) {
}; // Server side sort on username field.
ctx.setRequestControls(searchControl); tmpRequestControls.add(new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL));
}
if (pageSize > -1) {
// Server side paging.
tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
}
Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx.setRequestControls(requestControls);
// Search for the dn based on the username. // Search for the dn based on the username.
SearchControls searchControls = new SearchControls(); SearchControls searchControls = new SearchControls();
...@@ -232,25 +289,69 @@ public class LdapUserProvider implements UserProvider { ...@@ -232,25 +289,69 @@ public class LdapUserProvider implements UserProvider {
searchControls.setReturningAttributes(new String[] { manager.getUsernameField() }); searchControls.setReturningAttributes(new String[] { manager.getUsernameField() });
String filter = MessageFormat.format(manager.getSearchFilter(), "*"); String filter = MessageFormat.format(manager.getSearchFilter(), "*");
NamingEnumeration answer = ctx.search("", filter, searchControls); NamingEnumeration answer = ctx.search("", filter, searchControls);
while (answer.hasMoreElements()) { byte[] cookie = null;
// Get the next userID.
String username = (String)((SearchResult)answer.next()).getAttributes().get( // Run through all pages of results (one page is also possible ;) )
manager.getUsernameField()).get(); do {
// Escape username and add to results. // Examine all of the results on this page
usernames.add(JID.escapeNode(username));
}
// Add usernames found in alternate DN
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(searchControl);
answer = ctx2.search("", filter, searchControls);
while (answer.hasMoreElements()) { while (answer.hasMoreElements()) {
// Get the next userID. // Get the next userID.
String username = (String) ((SearchResult) answer.next()).getAttributes().get( String username = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getUsernameField()).get(); manager.getUsernameField()).get();
// Escape username and add to results. // Escape username and add to results.
usernames.add(JID.escapeNode(username)); usernames.add(JID.escapeNode(username));
} }
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
// Add usernames found in alternate DN
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(requestControls);
answer = ctx2.search("", filter, searchControls);
cookie = null;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all of the results on this page
while (answer.hasMoreElements()) {
// Get the next userID.
String username = (String) ((SearchResult) answer.next()).getAttributes().get(
manager.getUsernameField()).get();
// Escape username and add to results.
usernames.add(JID.escapeNode(username));
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
} }
// Close the enumeration. // Close the enumeration.
...@@ -279,25 +380,38 @@ public class LdapUserProvider implements UserProvider { ...@@ -279,25 +380,38 @@ public class LdapUserProvider implements UserProvider {
// Ignore. // Ignore.
} }
} }
// If client-side sorting is enabled, do it. // If client-side sorting is enabled, do it.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) { if (clientSideSort) {
Collections.sort(new ArrayList<String>(usernames)); Collections.sort(new ArrayList<String>(usernames));
} }
return usernames; return usernames;
} }
public Collection<User> getUsers() {
return getUsers(-1, -1);
}
public Collection<User> getUsers(int startIndex, int numResults) { public Collection<User> getUsers(int startIndex, int numResults) {
List<String> usernames = new ArrayList<String>(); List<String> usernames = new ArrayList<String>();
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
LdapContext ctx = null; LdapContext ctx = null;
LdapContext ctx2 = null; LdapContext ctx2 = null;
try { try {
ctx = manager.getContext(baseDN); ctx = manager.getContext(baseDN);
// Sort on username field. // Set up request controls, if appropriate.
Control[] searchControl = new Control[]{ List<Control> tmpRequestControls = new ArrayList<Control>();
new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL) if (!clientSideSort) {
}; // Server side sort on username field.
ctx.setRequestControls(searchControl); tmpRequestControls.add(new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL));
}
if (pageSize > -1) {
// Server side paging.
tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
}
Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx.setRequestControls(requestControls);
// Search for the dn based on the username. // Search for the dn based on the username.
SearchControls searchControls = new SearchControls(); SearchControls searchControls = new SearchControls();
...@@ -311,7 +425,7 @@ public class LdapUserProvider implements UserProvider { ...@@ -311,7 +425,7 @@ public class LdapUserProvider implements UserProvider {
searchControls.setReturningAttributes(new String[] { manager.getUsernameField() }); searchControls.setReturningAttributes(new String[] { manager.getUsernameField() });
// Limit results to those we'll need to process unless client-side sorting // Limit results to those we'll need to process unless client-side sorting
// is turned on. // is turned on.
if (!(Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting")))) { if (!clientSideSort) {
searchControls.setCountLimit(startIndex+numResults); searchControls.setCountLimit(startIndex+numResults);
} }
String filter = MessageFormat.format(manager.getSearchFilter(), "*"); String filter = MessageFormat.format(manager.getSearchFilter(), "*");
...@@ -320,11 +434,31 @@ public class LdapUserProvider implements UserProvider { ...@@ -320,11 +434,31 @@ public class LdapUserProvider implements UserProvider {
NamingEnumeration answer2 = null; NamingEnumeration answer2 = null;
if (alternateBaseDN != null) { if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN); ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(searchControl); ctx2.setRequestControls(requestControls);
answer2 = ctx2.search("", filter, searchControls); answer2 = ctx2.search("", filter, searchControls);
} }
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) { // If server side sort, we'll skip the initial ones we don't want, and stop when we've hit
// the amount we do want.
int skip = -1;
int lastRes = -1;
if (!clientSideSort) {
skip = startIndex;
lastRes = startIndex + numResults;
}
byte[] cookie = null;
int count = 0;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all results on this page
while (answer.hasMoreElements()) { while (answer.hasMoreElements()) {
count++;
if (skip > -1 && count < skip) {
continue;
}
if (lastRes > -1 && count > lastRes) {
break;
}
// Get the next userID. // Get the next userID.
String username = (String)((SearchResult)answer.next()).getAttributes().get( String username = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getUsernameField()).get(); manager.getUsernameField()).get();
...@@ -336,49 +470,36 @@ public class LdapUserProvider implements UserProvider { ...@@ -336,49 +470,36 @@ public class LdapUserProvider implements UserProvider {
// Escape username and add to results. // Escape username and add to results.
usernames.add(JID.escapeNode(username)); usernames.add(JID.escapeNode(username));
} }
if (alternateBaseDN != null) { // Examine the paged results control response
while (answer2.hasMoreElements()) { Control[] controls = ctx.getResponseControls();
// Get the next userID. if (controls != null) {
String username = (String) ((SearchResult) answer2.next()).getAttributes().get( for (Control control : controls) {
manager.getUsernameField()).get(); if (control instanceof PagedResultsResponseControl) {
// Remove usernameSuffix if set PagedResultsResponseControl prrc =
String suffix = manager.getUsernameSuffix(); (PagedResultsResponseControl) control;
if(suffix.length() > 0 && username.endsWith(suffix)) { cookie = prrc.getCookie();
username = username.substring(0,username.length()-suffix.length());
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
} }
// Escape username and add to results.
usernames.add(JID.escapeNode(username));
}
}
Collections.sort(new ArrayList<String>(usernames));
int endIndex = Math.min(startIndex + numResults, usernames.size()-1);
usernames = usernames.subList(startIndex, endIndex);
}
// Otherwise, only read in certain results.
else {
for (int i=0; i < startIndex; i++) {
if (answer.hasMoreElements()) {
answer.next();
} else if (alternateBaseDN != null && answer2.hasMoreElements()) {
answer2.next();
} else {
return Collections.emptyList();
} }
} }
// Now read in desired number of results (or stop if we run out of results). } while (cookie != null);
for (int i = 0; i < numResults; i++) { if (count <= lastRes && alternateBaseDN != null) {
if (answer.hasMoreElements()) { cookie = null;
// Get the next userID. // Run through all pages of results (one page is also possible ;) )
String username = (String)((SearchResult)answer.next()).getAttributes().get( do {
manager.getUsernameField()).get(); // Examine all results on this page
// Remove usernameSuffix if set while (answer2.hasMoreElements()) {
String suffix = manager.getUsernameSuffix(); count++;
if(suffix.length() > 0 && username.endsWith(suffix)) { if (skip > -1 && count < skip) {
username = username.substring(0,username.length()-suffix.length()); continue;
}
if (lastRes > -1 && count > lastRes) {
break;
} }
// Escape username and add to results.
usernames.add(JID.escapeNode(username));
} else if (alternateBaseDN != null && answer2.hasMoreElements()) {
// Get the next userID. // Get the next userID.
String username = (String) ((SearchResult) answer2.next()).getAttributes().get( String username = (String) ((SearchResult) answer2.next()).getAttributes().get(
manager.getUsernameField()).get(); manager.getUsernameField()).get();
...@@ -389,10 +510,30 @@ public class LdapUserProvider implements UserProvider { ...@@ -389,10 +510,30 @@ public class LdapUserProvider implements UserProvider {
} }
// Escape username and add to results. // Escape username and add to results.
usernames.add(JID.escapeNode(username)); usernames.add(JID.escapeNode(username));
} else {
break;
} }
} // Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
}
if (clientSideSort) {
// If we're doing client side sorting, we pulled everything in and now we sort and trim.
Collections.sort(new ArrayList<String>(usernames));
int endIndex = Math.min(startIndex + numResults, usernames.size()-1);
usernames = usernames.subList(startIndex, endIndex);
} }
// Close the enumeration. // Close the enumeration.
answer.close(); answer.close();
...@@ -467,6 +608,12 @@ public class LdapUserProvider implements UserProvider { ...@@ -467,6 +608,12 @@ public class LdapUserProvider implements UserProvider {
public Collection<User> findUsers(Set<String> fields, String query) public Collection<User> findUsers(Set<String> fields, String query)
throws UnsupportedOperationException throws UnsupportedOperationException
{
return findUsers(fields, query, -1, -1);
}
public Collection<User> findUsers(Set<String> fields, String query, int startIndex,
int numResults) throws UnsupportedOperationException
{ {
if (fields.isEmpty() || query == null || "".equals(query)) { if (fields.isEmpty() || query == null || "".equals(query)) {
return Collections.emptyList(); return Collections.emptyList();
...@@ -479,122 +626,26 @@ public class LdapUserProvider implements UserProvider { ...@@ -479,122 +626,26 @@ public class LdapUserProvider implements UserProvider {
if (!query.endsWith("*")) { if (!query.endsWith("*")) {
query = query + "*"; query = query + "*";
} }
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
List<String> usernames = new ArrayList<String>(); List<String> usernames = new ArrayList<String>();
LdapContext ctx = null; LdapContext ctx = null;
LdapContext ctx2 = null; LdapContext ctx2 = null;
try { try {
ctx = manager.getContext(baseDN); ctx = manager.getContext(baseDN);
// Sort on username field.
Control[] searchControl = new Control[]{
new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL)
};
ctx.setRequestControls(searchControl);
// Search for the dn based on the username. // Set up request controls, if appropriate.
SearchControls searchControls = new SearchControls(); List<Control> tmpRequestControls = new ArrayList<Control>();
// See if recursive searching is enabled. Otherwise, only search one level. if (!clientSideSort) {
if (manager.isSubTreeSearch()) { // Server side sort on username field.
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); tmpRequestControls.add(new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL));
}
else {
searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
}
searchControls.setReturningAttributes(new String[] { manager.getUsernameField() });
String searchFilter = MessageFormat.format(manager.getSearchFilter(),"*");
StringBuilder filter = new StringBuilder();
//Add the global search filter so only those users the directory administrator wants to include
//are returned from the directory
filter.append("(&(");
filter.append(searchFilter);
filter.append(")");
if (fields.size() > 1) {
filter.append("(|");
}
for (String field:fields) {
String attribute = searchFields.get(field);
filter.append("(").append(attribute).append("=").append(query).append(")");
}
if (fields.size() > 1) {
filter.append(")");
} }
filter.append(")"); if (pageSize > -1) {
NamingEnumeration answer = ctx.search("", filter.toString(), searchControls); // Server side paging.
while (answer.hasMoreElements()) { tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
// Get the next userID.
String username = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getUsernameField()).get();
// Escape username and add to results.
usernames.add(JID.escapeNode(username));
} }
if (alternateBaseDN != null) { Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx2 = manager.getContext(alternateBaseDN); ctx.setRequestControls(requestControls);
ctx2.setRequestControls(searchControl);
answer = ctx2.search("", filter.toString(), searchControls);
while (answer.hasMoreElements()) {
// Get the next userID.
String username = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getUsernameField()).get();
// Remove usernameSuffix if set
String suffix = manager.getUsernameSuffix();
if(suffix.length() > 0 && username.endsWith(suffix)) {
username = username.substring(0,username.length()-suffix.length());
}
// Escape username and add to results.
usernames.add(JID.escapeNode(username));
}
}
// Close the enumeration.
answer.close();
// If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) {
Collections.sort(usernames);
}
}
catch (Exception e) {
Log.error(e);
}
finally {
try {
if (ctx != null) {
ctx.setRequestControls(null);
ctx.close();
}
}
catch (Exception ignored) {
// Ignore.
}
try {
if (ctx2 != null) {
ctx2.setRequestControls(null);
ctx2.close();
}
}
catch (Exception ignored) {
// Ignore.
}
}
return new UserCollection(usernames.toArray(new String[usernames.size()]));
}
public Collection<User> findUsers(Set<String> fields, String query, int startIndex,
int numResults) throws UnsupportedOperationException
{
if (fields.isEmpty()) {
return Collections.emptyList();
}
if (!searchFields.keySet().containsAll(fields)) {
throw new IllegalArgumentException("Search fields " + fields + " are not valid.");
}
List<String> usernames = new ArrayList<String>();
LdapContext ctx = null;
LdapContext ctx2 = null;
try {
ctx = manager.getContext(baseDN);
// Sort on username field.
Control[] searchControl = new Control[]{
new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL)
};
ctx.setRequestControls(searchControl);
// Search for the dn based on the username. // Search for the dn based on the username.
SearchControls searchControls = new SearchControls(); SearchControls searchControls = new SearchControls();
...@@ -624,60 +675,103 @@ public class LdapUserProvider implements UserProvider { ...@@ -624,60 +675,103 @@ public class LdapUserProvider implements UserProvider {
filter.append(")"); filter.append(")");
} }
filter.append(")"); filter.append(")");
// TODO: used paged results if supported by LDAP server.
NamingEnumeration answer = ctx.search("", filter.toString(), searchControls); NamingEnumeration answer = ctx.search("", filter.toString(), searchControls);
NamingEnumeration answer2 = null; // If server side sort, we'll skip the initial ones we don't want, and stop when we've hit
if(alternateBaseDN != null) { // the amount we do want.
ctx2 = manager.getContext(alternateBaseDN); int skip = -1;
ctx2.setRequestControls(searchControl); int lastRes = -1;
answer2 = ctx2.search("", filter.toString(), searchControls); if (!clientSideSort) {
} skip = startIndex;
for (int i=0; i < startIndex; i++) { lastRes = startIndex + numResults;
if (answer.hasMoreElements()) {
answer.next(); }
} byte[] cookie = null;
else if (alternateBaseDN != null && answer2.hasMoreElements()) { int count = 0;
answer2.next(); // Run through all pages of results (one page is also possible ;) )
} do {
else { // Examine all results on this page
return Collections.emptyList(); while (answer.hasMoreElements()) {
} count++;
} if (skip > -1 && count < skip) {
// Now read in desired number of results (or stop if we run out of results). continue;
for (int i = 0; i < numResults; i++) { }
if (answer.hasMoreElements()) { if (lastRes > -1 && count > lastRes) {
break;
}
// Get the next userID. // Get the next userID.
String username = (String)((SearchResult)answer.next()).getAttributes().get( String username = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getUsernameField()).get(); manager.getUsernameField()).get();
// Remove usernameSuffix if set
String suffix = manager.getUsernameSuffix();
if(suffix.length() > 0 && username.endsWith(suffix)) {
username = username.substring(0,username.length()-suffix.length());
}
// Escape username and add to results. // Escape username and add to results.
usernames.add(JID.escapeNode(username)); usernames.add(JID.escapeNode(username));
} }
else if (alternateBaseDN != null && answer2.hasMoreElements()) { // Examine the paged results control response
// Get the next userID. Control[] controls = ctx.getResponseControls();
String username = (String)((SearchResult)answer2.next()).getAttributes().get( if (controls != null) {
manager.getUsernameField()).get(); for (Control control : controls) {
// Remove usernameSuffix if set if (control instanceof PagedResultsResponseControl) {
String suffix = manager.getUsernameSuffix(); PagedResultsResponseControl prrc =
if(suffix.length() > 0 && username.endsWith(suffix)) { (PagedResultsResponseControl) control;
username = username.substring(0,username.length()-suffix.length()); cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
} }
// Escape username and add to results.
usernames.add(JID.escapeNode(username));
}
else {
break;
} }
} while (cookie != null);
if (count <= lastRes && alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(requestControls);
answer = ctx2.search("", filter.toString(), searchControls);
cookie = null;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all results on this page
while (answer.hasMoreElements()) {
count++;
if (skip > -1 && count < skip) {
continue;
}
if (lastRes > -1 && count > lastRes) {
break;
}
// Get the next userID.
String username = (String)((SearchResult)answer.next()).getAttributes().get(
manager.getUsernameField()).get();
// Remove usernameSuffix if set
String suffix = manager.getUsernameSuffix();
if(suffix.length() > 0 && username.endsWith(suffix)) {
username = username.substring(0,username.length()-suffix.length());
}
// Escape username and add to results.
usernames.add(JID.escapeNode(username));
}
// Examine the paged results control response
Control[] controls = ctx.getResponseControls();
if (controls != null) {
for (Control control : controls) {
if (control instanceof PagedResultsResponseControl) {
PagedResultsResponseControl prrc =
(PagedResultsResponseControl) control;
cookie = prrc.getCookie();
// Re-activate paged results
ctx.setRequestControls(new Control[]{
new PagedResultsControl(pageSize, cookie, Control.CRITICAL) });
break;
}
}
}
} while (cookie != null);
} }
// Close the enumeration. // Close the enumeration.
answer.close(); answer.close();
// If client-side sorting is enabled, sort. // If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) { if (clientSideSort) {
Collections.sort(usernames); Collections.sort(usernames);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment