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;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.JiveConstants;
import org.xmpp.packet.JID;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.SortControl;
import javax.naming.ldap.*;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Matcher;
......@@ -42,14 +40,20 @@ import java.util.regex.Pattern;
public class LdapGroupProvider implements GroupProvider {
private LdapManager manager;
private String baseDN;
private String alternateBaseDN;
private UserManager userManager;
private String[] standardAttributes;
private int groupCount = -1;
private long expiresStamp = System.currentTimeMillis();
/**
* Constructs a new LDAP group provider.
*/
public LdapGroupProvider() {
manager = LdapManager.getInstance();
baseDN = manager.getBaseDN();
alternateBaseDN = manager.getAlternateBaseDN();
userManager = UserManager.getInstance();
standardAttributes = new String[3];
standardAttributes[0] = manager.getGroupNameField();
......@@ -78,34 +82,18 @@ public class LdapGroupProvider implements GroupProvider {
}
public Group getGroup(String groupName) throws GroupNotFoundException {
Collection<Group> groups;
LdapContext ctx = null;
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.
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();
}
return processGroup(ctx, attrs);
}
catch (Exception e) {
Log.error(e);
throw new GroupNotFoundException(e);
throw new GroupNotFoundException("Group with name " + groupName + " not found.", e);
}
finally {
try {
......@@ -118,12 +106,6 @@ public class LdapGroupProvider implements GroupProvider {
// 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 {
if (manager.isDebugEnabled()) {
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 pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
LdapContext ctx = null;
LdapContext ctx2 = null;
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();
// See if recursive searching is enabled. Otherwise, only search one level.
......@@ -171,9 +168,62 @@ public class LdapGroupProvider implements GroupProvider {
searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() });
String filter = MessageFormat.format(manager.getGroupSearchFilter(), "*");
NamingEnumeration answer = ctx.search("", filter, searchControls);
while (answer.hasMoreElements()) {
answer.next();
count++;
byte[] cookie = null;
// 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.
answer.close();
......@@ -187,78 +237,45 @@ public class LdapGroupProvider implements GroupProvider {
ctx.setRequestControls(null);
ctx.close();
}
if (ctx2 != null) {
ctx2.setRequestControls(null);
ctx2.close();
}
}
catch (Exception ignored) {
// Ignore.
}
}
this.groupCount = count;
this.expiresStamp = System.currentTimeMillis() + JiveConstants.MINUTE *5;
return count;
}
public Collection<String> getGroupNames() {
List<String> groupNames = new ArrayList<String>();
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;
return getGroupNames(-1, -1);
}
public Collection<String> getGroupNames(int startIndex, int numResults) {
List<String> groupNames = new ArrayList<String>();
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
LdapContext ctx = null;
LdapContext ctx2 = 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);
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);
SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level.
......@@ -270,34 +287,102 @@ public class LdapGroupProvider implements GroupProvider {
}
searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() });
String filter = MessageFormat.format(manager.getGroupSearchFilter(), "*");
// TODO: used paged results if supported by LDAP server.
NamingEnumeration answer = ctx.search("", filter, searchControls);
for (int i=0; i < startIndex; i++) {
if (answer.hasMoreElements()) {
answer.next();
}
else {
return Collections.emptyList();
}
}
// Now read in desired number of results (or stop if we run out of results).
for (int i = 0; i < numResults; i++) {
if (answer.hasMoreElements()) {
// 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 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));
}
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);
// 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.
answer.close();
// If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) {
if (clientSideSort) {
Collections.sort(groupNames);
}
}
......@@ -310,6 +395,10 @@ public class LdapGroupProvider implements GroupProvider {
ctx.setRequestControls(null);
ctx.close();
}
if (ctx2 != null) {
ctx2.setRequestControls(null);
ctx2.close();
}
}
catch (Exception ignored) {
// Ignore.
......@@ -344,10 +433,27 @@ public class LdapGroupProvider implements GroupProvider {
return Collections.emptyList();
}
// Perform the LDAP query
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
List<String> groupNames = new ArrayList<String>();
LdapContext ctx = null;
LdapContext ctx2 = null;
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.
SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level.
......@@ -368,17 +474,71 @@ public class LdapGroupProvider implements GroupProvider {
Log.debug("Trying to find group names for user: " + user + " using query: " + filter.toString());
}
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));
byte[] 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);
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.
answer.close();
// If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) {
if (clientSideSort) {
Collections.sort(groupNames);
}
}
......@@ -392,6 +552,10 @@ public class LdapGroupProvider implements GroupProvider {
ctx.setRequestControls(null);
ctx.close();
}
if (ctx2 != null) {
ctx2.setRequestControls(null);
ctx2.close();
}
}
catch (Exception ignored) {
// Ignore.
......@@ -448,66 +612,7 @@ public class LdapGroupProvider implements GroupProvider {
}
public Collection<String> search(String query) {
if (query == null || "".equals(query)) {
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;
return search(query, -1, -1);
}
public Collection<String> search(String query, int startIndex, int numResults) {
......@@ -520,14 +625,25 @@ public class LdapGroupProvider implements GroupProvider {
query = query + "*";
}
List<String> groupNames = new ArrayList<String>();
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
LdapContext ctx = null;
LdapContext ctx2 = null;
try {
ctx = manager.getContext();
// Sort on username field.
Control[] searchControl = new Control[]{
new SortControl(new String[]{manager.getGroupNameField()}, Control.NONCRITICAL)
};
ctx.setRequestControls(searchControl);
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.
SearchControls searchControls = new SearchControls();
......@@ -541,34 +657,98 @@ public class LdapGroupProvider implements GroupProvider {
searchControls.setReturningAttributes(new String[] { manager.getGroupNameField() });
StringBuilder filter = new StringBuilder();
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);
for (int i=0; i < startIndex; i++) {
if (answer.hasMoreElements()) {
answer.next();
}
else {
return Collections.emptyList();
}
}
// Now read in desired number of results (or stop if we run out of results).
for (int i = 0; i < numResults; i++) {
if (answer.hasMoreElements()) {
// 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()) {
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));
}
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 (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.
answer.close();
// If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) {
if (clientSideSort) {
Collections.sort(groupNames);
}
}
......@@ -581,6 +761,10 @@ public class LdapGroupProvider implements GroupProvider {
ctx.setRequestControls(null);
ctx.close();
}
if (ctx2 != null) {
ctx2.setRequestControls(null);
ctx2.close();
}
}
catch (Exception ignored) {
// Ignore.
......@@ -598,143 +782,96 @@ public class LdapGroupProvider implements GroupProvider {
*
* @param answer LDAP search result.
* @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 {
if (manager.isDebugEnabled()) {
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 {
TreeMap<String, Group> groups = new TreeMap<String, Group>();
ctx = manager.getContext();
ctx = manager.getContext(baseDN);
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);
// 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);
byte[] cookie = null;
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() + "=)([^,]+)(.+)");
while (answer.hasMoreElements()) {
String name = "";
try {
Attributes a = answer.nextElement().getAttributes();
String description;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all results on this page
while (answer.hasMoreElements()) {
String name = "";
try {
name = ((String)((a.get(manager.getGroupNameField())).get()));
description =
((String)((a.get(manager.getGroupDescriptionField())).get()));
Group group = processGroup(ctx, answer.nextElement().getAttributes());
name = group.getName();
groups.put(name, group);
}
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());
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);
}
}
}
// 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;
}
// 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) {
e.printStackTrace();
if (manager.isDebugEnabled()) {
Log.debug("LdapGroupProvider: Error while populating group, " + name + ".", e);
} while (cookie != null);
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
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()) {
Log.debug("LdapGroupProvider: Finished populating group(s) with users.");
......@@ -742,15 +879,143 @@ public class LdapGroupProvider implements GroupProvider {
return groups.values();
}
catch (Exception e) {
Log.error("Exception while attempting to populate groups and members: ", e);
return new ArrayList<Group>();
}
finally {
try {
if (ctx != null) {
ctx.close();
}
if (ctx2 != null) {
ctx2.close();
}
}
catch (Exception e) {
// 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;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
......@@ -28,6 +29,7 @@ import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.text.MessageFormat;
/**
* Centralized administration of LDAP connections. The {@link #getInstance()} method
......@@ -146,6 +148,7 @@ public class LdapManager {
private String searchFilter = null;
private boolean subTreeSearch;
private boolean encloseUserDN;
private boolean encloseGroupDN;
private String groupNameField;
private String groupMemberField;
......@@ -154,6 +157,7 @@ public class LdapManager {
private String groupSearchFilter = null;
private Pattern userDNPattern;
private Pattern groupDNPattern;
private Map<String, String> properties;
......@@ -282,8 +286,15 @@ public class LdapManager {
if (encloseUserStr != null) {
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 "
userDNPattern = Pattern.compile("(=)([^\\\"][^=]*[^\\\"])(?:,|$)");
// Set the pattern to use to wrap groupDNs values "
groupDNPattern = Pattern.compile("(=)([^\\\"][^=]*[^\\\"])(?:,|$)");
this.initialContextFactory = properties.get("ldap.initialContextFactory");
if (initialContextFactory != null) {
try {
......@@ -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.
* If the encoding fails then the URL will contain the raw base dn.
......@@ -991,6 +1152,32 @@ public class LdapManager {
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.
* Searches will performed on the entire sub-tree under the admin DN.
......
......@@ -20,9 +20,7 @@ import org.xmpp.packet.JID;
import javax.naming.NamingEnumeration;
import javax.naming.directory.*;
import javax.naming.ldap.Control;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.SortControl;
import javax.naming.ldap.*;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.*;
......@@ -144,10 +142,21 @@ public class LdapUserProvider implements UserProvider {
return userCount;
}
int count = 0;
DirContext ctx = null;
DirContext ctx2 = null;
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
LdapContext ctx = null;
LdapContext ctx2 = null;
try {
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.
SearchControls searchControls = new SearchControls();
// See if recursive searching is enabled. Otherwise, only search one level.
......@@ -160,18 +169,62 @@ public class LdapUserProvider implements UserProvider {
searchControls.setReturningAttributes(new String[] { manager.getUsernameField() });
String filter = MessageFormat.format(manager.getSearchFilter(), "*");
NamingEnumeration answer = ctx.search("", filter, searchControls);
while (answer.hasMoreElements()) {
count++;
answer.nextElement();
}
// Add count of users found in alternate DN
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
answer = ctx2.search("", filter, searchControls);
byte[] 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);
// 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.
answer.close();
......@@ -202,23 +255,27 @@ public class LdapUserProvider implements UserProvider {
return count;
}
public Collection<User> getUsers() {
Collection<String> usernames = getUsernames();
return new UserCollection(usernames.toArray(new String[usernames.size()]));
}
public Collection<String> getUsernames() {
Set<String> usernames = new HashSet<String>();
LdapContext ctx = null;
LdapContext ctx2 = null;
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
try {
ctx = manager.getContext(baseDN);
// Sort on username field.
Control[] searchControl = new Control[]{
new SortControl(new String[]{manager.getUsernameField()}, Control.NONCRITICAL)
};
ctx.setRequestControls(searchControl);
// 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 username.
SearchControls searchControls = new SearchControls();
......@@ -232,25 +289,69 @@ public class LdapUserProvider implements UserProvider {
searchControls.setReturningAttributes(new String[] { manager.getUsernameField() });
String filter = MessageFormat.format(manager.getSearchFilter(), "*");
NamingEnumeration answer = ctx.search("", filter, searchControls);
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));
}
// Add usernames found in alternate DN
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(searchControl);
answer = ctx2.search("", filter, searchControls);
byte[] 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(
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);
// 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.
......@@ -279,25 +380,38 @@ public class LdapUserProvider implements UserProvider {
// Ignore.
}
}
// If client-side sorting is enabled, do it.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) {
// If client-side sorting is enabled, do it.
if (clientSideSort) {
Collections.sort(new ArrayList<String>(usernames));
}
return usernames;
}
public Collection<User> getUsers() {
return getUsers(-1, -1);
}
public Collection<User> getUsers(int startIndex, int numResults) {
List<String> usernames = new ArrayList<String>();
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
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);
// 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 username.
SearchControls searchControls = new SearchControls();
......@@ -311,7 +425,7 @@ public class LdapUserProvider implements UserProvider {
searchControls.setReturningAttributes(new String[] { manager.getUsernameField() });
// Limit results to those we'll need to process unless client-side sorting
// is turned on.
if (!(Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting")))) {
if (!clientSideSort) {
searchControls.setCountLimit(startIndex+numResults);
}
String filter = MessageFormat.format(manager.getSearchFilter(), "*");
......@@ -320,11 +434,31 @@ public class LdapUserProvider implements UserProvider {
NamingEnumeration answer2 = null;
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(searchControl);
ctx2.setRequestControls(requestControls);
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()) {
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();
......@@ -336,49 +470,36 @@ public class LdapUserProvider implements UserProvider {
// Escape username and add to results.
usernames.add(JID.escapeNode(username));
}
if (alternateBaseDN != null) {
while (answer2.hasMoreElements()) {
// Get the next userID.
String username = (String) ((SearchResult) answer2.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());
// 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;
}
// 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).
for (int i = 0; i < numResults; i++) {
if (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());
} while (cookie != null);
if (count <= lastRes && alternateBaseDN != null) {
cookie = null;
// Run through all pages of results (one page is also possible ;) )
do {
// Examine all results on this page
while (answer2.hasMoreElements()) {
count++;
if (skip > -1 && count < skip) {
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.
String username = (String) ((SearchResult) answer2.next()).getAttributes().get(
manager.getUsernameField()).get();
......@@ -389,10 +510,30 @@ public class LdapUserProvider implements UserProvider {
}
// Escape username and add to results.
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.
answer.close();
......@@ -467,6 +608,12 @@ public class LdapUserProvider implements UserProvider {
public Collection<User> findUsers(Set<String> fields, String query)
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)) {
return Collections.emptyList();
......@@ -479,122 +626,26 @@ public class LdapUserProvider implements UserProvider {
if (!query.endsWith("*")) {
query = query + "*";
}
int pageSize = JiveGlobals.getXMLProperty("ldap.pagedResultsSize", -1);
Boolean clientSideSort = JiveGlobals.getXMLProperty("ldap.clientSideSorting", false);
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.
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.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(")");
// 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));
}
filter.append(")");
NamingEnumeration answer = ctx.search("", filter.toString(), searchControls);
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));
if (pageSize > -1) {
// Server side paging.
tmpRequestControls.add(new PagedResultsControl(pageSize, Control.NONCRITICAL));
}
if (alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
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);
Control[] requestControls = tmpRequestControls.toArray(new Control[tmpRequestControls.size()]);
ctx.setRequestControls(requestControls);
// Search for the dn based on the username.
SearchControls searchControls = new SearchControls();
......@@ -624,60 +675,103 @@ public class LdapUserProvider implements UserProvider {
filter.append(")");
}
filter.append(")");
// TODO: used paged results if supported by LDAP server.
NamingEnumeration answer = ctx.search("", filter.toString(), searchControls);
NamingEnumeration answer2 = null;
if(alternateBaseDN != null) {
ctx2 = manager.getContext(alternateBaseDN);
ctx2.setRequestControls(searchControl);
answer2 = ctx2.search("", filter.toString(), searchControls);
}
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).
for (int i = 0; i < numResults; i++) {
if (answer.hasMoreElements()) {
// 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()) {
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));
}
else if (alternateBaseDN != null && answer2.hasMoreElements()) {
// Get the next userID.
String username = (String)((SearchResult)answer2.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());
// 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;
}
}
// 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.
answer.close();
// If client-side sorting is enabled, sort.
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.clientSideSorting"))) {
if (clientSideSort) {
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