Commit fd903160 authored by Matt Tucker's avatar Matt Tucker Committed by matt

Code cleanup, improved how search filters work (JM-792), added group searching (JM-771) .

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@4798 b35dd754-fafc-0310-a699-88a17e54d16e
parent 4b25e8aa
......@@ -145,9 +145,17 @@
as many fields as you'd like using comma-delimited "DisplayName/Field" pairs. You should
ensure that any fields used for searching are properly indexed so that searches return
quickly.</li>
<li>ldap.searchFilter -- the search filter that should be used when loading users. If this
property is not set, the default search will be for users that have the attribute specified by
ldap.usernameField.
<li>ldap.searchFilter -- an optional search filter to append to the default filter when
loading users. The default search filter is created using the attribute specified by
ldap.usernameField. For example, if the username field is "uid", then the default search
filter would be "(uid={0})" where {0} is dynamically replaced with the username being searched
for.
<br/><br/>
The most common usage of a search filter is to limit the entries that are users
based on objectClass. For example, a reasonable search filter for a default Active Directory
installation is "(objectClass=organizationalPerson)". When combined with the default
filter, the actual search executed would be
"(&(sAMAccountName={0})(objectClass=organizationalPerson))".</li>
<br><br>
<b>Group Settings</b><br><br>
......@@ -161,14 +169,27 @@
<li>ldap.groupDescriptionField -- the field name that holds the description a group. If this
property is not set, the default value is <tt>description</tt>.</li>
<li>ldap.posixMode <font color="red"><b>**</b></font> -- a value of "true" means that users are stored within the group by their
user name alone. A value of "false" means that users are stored by their entire DN within
the group. If this property is not set, the default value is <tt>false</tt>. <b>Note:</b>
the posix mode must be set correctly for your server in order for group integration to
work.</li>
<li>ldap.groupSearchFilter -- the search filter that should be used when loading groups. If this
property is not set, the default value is <tt>("ldap.groupNameField"={0})</tt>.</li>
<li>ldap.posixMode <font color="red"><b>**</b></font> -- a value of "true" means that users are
stored within the group by their user name alone. A value of "false" means that users are
stored by their entire DN within the group. If this property is not set, the default value
is <tt>false</tt>. The posix mode must be set correctly for your server in
order for group integration to work. Posix modes for common LDAP servers:
<ul>
<li>ActiveDirectory: false</li>
</ul>
</li>
<li>ldap.groupSearchFilter -- an optional search filter to append to the default filter when
loading groups. The default group search filter is created using the attribute specified
by ldap.groupNameField. For example, if the group name field is "cn", then the default
group search filter would be "(cn={0})" where {0} is dynamically replaced with the group
name being searched for.
<br/><br/>
The most common usage of a search filter is to limit the entries that are groups
based on objectClass. For example, a reasonable search filter for a default Active Directory
installation is "(objectClass=group)". When combined with the default
filter, the actual search executed would be
"(&(cn={0})(objectClass=group))".</li>
<br><br>
<b>Connection Settings</b><br><br>
......
......@@ -364,8 +364,12 @@ public class XMLProperties {
* @param value the new value for the property.
*/
public synchronized void setProperty(String name, String value) {
if (name == null) return;
if (value == null) value = "";
if (name == null) {
return;
}
if (value == null) {
value = "";
}
// Set cache correctly with prop name and value.
propertyCache.put(name, value);
......
......@@ -86,7 +86,7 @@ public interface AuthProvider {
*
* @param username the username of the user.
* @return the user's password.
* @throws UserNotFoundException if the given user could not be loaded.
* @throws UserNotFoundException if the given user's password could not be loaded.
* @throws UnsupportedOperationException if the provider does not
* support the operation (this is an optional operation).
*/
......
......@@ -13,17 +13,12 @@ package org.jivesoftware.wildfire.group;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.wildfire.XMPPServer;
import org.xmpp.packet.JID;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.sql.*;
import java.util.*;
/**
* Database implementation of the GroupManager interface.
......@@ -81,9 +76,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
Collection<JID> members = getMembers(name, false);
......@@ -111,9 +112,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
Collection<JID> members = getMembers(name, false);
......@@ -138,9 +145,15 @@ public class DefaultGroupProvider implements GroupProvider {
throw new GroupNotFoundException();
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
}
......@@ -173,7 +186,10 @@ public class DefaultGroupProvider implements GroupProvider {
abortTransaction = true;
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
DbConnectionManager.closeTransactionConnection(con, abortTransaction);
}
......@@ -205,7 +221,10 @@ public class DefaultGroupProvider implements GroupProvider {
abortTransaction = true;
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
DbConnectionManager.closeTransactionConnection(con, abortTransaction);
}
......@@ -228,9 +247,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
return count;
......@@ -253,9 +278,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
return new GroupCollection(groupNames.toArray(new String[groupNames.size()]));
......@@ -285,9 +316,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
return new GroupCollection(groupNames.toArray(new String[groupNames.size()]));
......@@ -311,9 +348,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
return new GroupCollection(groupNames.toArray(new String[groupNames.size()]));
......@@ -334,9 +377,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
}
......@@ -356,9 +405,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
}
......@@ -377,9 +432,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
}
......@@ -388,6 +449,88 @@ public class DefaultGroupProvider implements GroupProvider {
return false;
}
public Collection<Group> search(String query) {
if (query == null || "".equals(query)) {
return Collections.emptyList();
}
// SQL LIKE queries don't map directly into a keyword/wildcard search like we want.
// Therefore, we do a best approximiation by replacing '*' with '%' and then
// surrounding the whole query with two '%'. This will return more data than desired,
// but is better than returning less data than desired.
query = "%" + query.replace('*', '%') + "%";
if (query.endsWith("%%")) {
query = query.substring(0, query.length()-1);
}
List<String> groupNames = new ArrayList<String>();
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
con = DbConnectionManager.getConnection();
stmt = con.createStatement();
StringBuilder sql = new StringBuilder();
sql.append("SELECT groupName FROM jiveGroup WHERE groupName LIKE '").append(
StringUtils.escapeForSQL(query)).append("' ORDER BY groupName");
rs = stmt.executeQuery(sql.toString());
while (rs.next()) {
groupNames.add(rs.getString(1));
}
}
catch (SQLException e) {
Log.error(e);
}
finally {
DbConnectionManager.closeConnection(rs, stmt, con);
}
return new GroupCollection(groupNames.toArray(new String[groupNames.size()]));
}
public Collection<Group> search(String query, int startIndex, int numResults) {
if (query == null || "".equals(query)) {
return Collections.emptyList();
}
// SQL LIKE queries don't map directly into a keyword/wildcard search like we want.
// Therefore, we do a best approximiation by replacing '*' with '%' and then
// surrounding the whole query with two '%'. This will return more data than desired,
// but is better than returning less data than desired.
query = "%" + query.replace('*', '%') + "%";
if (query.endsWith("%%")) {
query = query.substring(0, query.length()-1);
}
List<String> groupNames = new ArrayList<String>();
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
con = DbConnectionManager.getConnection();
stmt = DbConnectionManager.createScrollableStatement(con);
StringBuilder sql = new StringBuilder();
sql.append("SELECT groupName FROM jiveGroup WHERE groupName LIKE '").append(
StringUtils.escapeForSQL(query)).append("' ORDER BY groupName");
rs = stmt.executeQuery(sql.toString());
DbConnectionManager.setFetchSize(rs, startIndex + numResults);
DbConnectionManager.scrollResultSet(rs, startIndex);
int count = 0;
while (rs.next() && count < numResults) {
groupNames.add(rs.getString(1));
count++;
}
}
catch (SQLException e) {
Log.error(e);
}
finally {
DbConnectionManager.closeConnection(rs, stmt, con);
}
return new GroupCollection(groupNames.toArray(new String[groupNames.size()]));
}
public boolean isSearchSupported() {
return true;
}
private Collection<JID> getMembers(String groupName, boolean adminsOnly) {
List<JID> members = new ArrayList<JID>();
Connection con = null;
......@@ -422,9 +565,15 @@ public class DefaultGroupProvider implements GroupProvider {
Log.error(e);
}
finally {
try { if (pstmt != null) pstmt.close(); }
try {
if (pstmt != null) {
pstmt.close();
} }
catch (Exception e) { Log.error(e); }
try { if (con != null) con.close(); }
try {
if (con != null) {
con.close();
} }
catch (Exception e) { Log.error(e); }
}
return members;
......
......@@ -289,6 +289,57 @@ public class GroupManager {
}
}
/**
* Returns true if groups are read-only.
*
* @return true if groups are read-only.
*/
public boolean isReadOnly() {
return provider.isReadOnly();
}
/**
* Returns true if searching for groups is supported.
*
* @return true if searching for groups are supported.
*/
public boolean isSearchSupported() {
return provider.isSearchSupported();
}
/**
* Returns the groups that match the search. The search is over group names and
* implicitly uses wildcard matching (although the exact search semantics are left
* up to each provider implementation). For example, a search for "HR" should match
* the groups "HR", "HR Department", and "The HR People".<p>
*
* Before searching or showing a search UI, use the {@link #isSearchSupported} method
* to ensure that searching is supported.
*
* @param query the search string for group names.
* @return all groups that match the search.
*/
public Collection<Group> search(String query) {
return provider.search(query);
}
/**
* Returns the groups that match the search given a start index and desired number
* of results. The search is over group names and implicitly uses wildcard matching
* (although the exact search semantics are left up to each provider implementation).
* For example, a search for "HR" should match the groups "HR", "HR Department", and
* "The HR People".<p>
*
* Before searching or showing a search UI, use the {@link #isSearchSupported} method
* to ensure that searching is supported.
*
* @param query the search string for group names.
* @return all groups that match the search.
*/
public Collection<Group> search(String query, int startIndex, int numResults) {
return provider.search(query, startIndex, numResults);
}
/**
* Returns the configured group provider. Note that this method has special access
* privileges since only a few certain classes need to access the provider directly.
......
......@@ -168,5 +168,40 @@ public interface GroupProvider {
*
* @return true if the user provider is read-only.
*/
public boolean isReadOnly();
boolean isReadOnly();
/**
* Returns the groups that match the search. The search is over group names and
* implicitly uses wildcard matching (although the exact search semantics are left
* up to each provider implementation). For example, a search for "HR" should match
* the groups "HR", "HR Department", and "The HR People".<p>
*
* Before searching or showing a search UI, use the {@link #isSearchSupported} method
* to ensure that searching is supported.
*
* @param query the search string for group names.
* @return all groups that match the search.
*/
Collection<Group> search(String query);
/**
* Returns the groups that match the search given a start index and desired number of results.
* The search is over group names and implicitly uses wildcard matching (although the
* exact search semantics are left up to each provider implementation). For example, a
* search for "HR" should match the groups "HR", "HR Department", and "The HR People".<p>
*
* Before searching or showing a search UI, use the {@link #isSearchSupported} method
* to ensure that searching is supported.
*
* @param query the search string for group names.
* @return all groups that match the search.
*/
Collection<Group> search(String query, int startIndex, int numResults);
/**
* Returns true if group searching is supported by the provider.
*
* @return true if searching is supported.
*/
boolean isSearchSupported();
}
\ No newline at end of file
......@@ -384,4 +384,12 @@ public class JDBCGroupProvider implements GroupProvider {
public boolean isReadOnly() {
return true;
}
public Collection<Group> search(String query) {
return Collections.emptyList();
}
public boolean supportsSearch() {
return false;
}
}
\ No newline at end of file
......@@ -44,7 +44,7 @@ public class LdapAuthProvider implements AuthProvider {
public LdapAuthProvider() {
manager = LdapManager.getInstance();
if (Boolean.valueOf(JiveGlobals.getXMLProperty("ldap.authCache.enabled")).booleanValue()) {
if (JiveGlobals.getXMLProperty("ldap.authCache.enabled", false)) {
int maxSize = JiveGlobals.getXMLProperty("ldap.authCache.size", 512*1024);
long maxLifetime = (long)JiveGlobals.getXMLProperty("ldap.authCache.maxLifetime",
(int)JiveConstants.HOUR * 2);
......@@ -83,8 +83,9 @@ public class LdapAuthProvider implements AuthProvider {
// example if the baseDN was set to "ou=People, o=jivesoftare, o=com"
// then we would be able to directly load users from that node
// of the LDAP tree. However, it's a poor assumption that only a
// flat structure will be used. Therefore, we search all subtrees
// of the baseDN for the username. So, if the baseDN is set to
// flat structure will be used. Therefore, we search all sub-trees
// of the baseDN for the username (assuming the user has not disabled
// sub-tree searching). So, if the baseDN is set to
// "o=jivesoftware, o=com" then a search will include the "People"
// node as well all the others under the base.
userDN = manager.findUserDN(username);
......
......@@ -13,10 +13,12 @@ package org.jivesoftware.wildfire.ldap;
import org.jivesoftware.util.JiveConstants;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.wildfire.XMPPServer;
import org.jivesoftware.wildfire.group.Group;
import org.jivesoftware.wildfire.group.GroupNotFoundException;
import org.jivesoftware.wildfire.group.GroupProvider;
import org.jivesoftware.wildfire.group.GroupCollection;
import org.jivesoftware.wildfire.user.UserManager;
import org.jivesoftware.wildfire.user.UserNotFoundException;
import org.xmpp.packet.JID;
......@@ -25,10 +27,14 @@ import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.*;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.SortControl;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.IOException;
/**
* LDAP implementation of the GroupProvider interface. All data in the directory is treated as read-only so any set
......@@ -86,7 +92,7 @@ public class LdapGroupProvider implements GroupProvider {
try {
groups = populateGroups(searchForGroups(searchFilter, standardAttributes));
}
catch (NamingException e) {
catch (Exception e) {
Log.error("Error populating groups from LDAP", e);
throw new GroupNotFoundException("Error populating groups from LDAP", e);
}
......@@ -152,7 +158,7 @@ public class LdapGroupProvider implements GroupProvider {
this.groupCount = count;
this.expiresStamp = System.currentTimeMillis() + JiveConstants.MINUTE * 5;
}
catch (NamingException ex) {
catch (Exception ex) {
Log.error("Error searching for groups in LDAP", ex);
}
return count;
......@@ -163,7 +169,7 @@ public class LdapGroupProvider implements GroupProvider {
try {
return populateGroups(searchForGroups(filter, standardAttributes));
}
catch (NamingException ex) {
catch (Exception ex) {
return Collections.emptyList();
}
}
......@@ -209,7 +215,7 @@ public class LdapGroupProvider implements GroupProvider {
try {
groups.addAll(populateGroups(searchForGroups(searchFilter, standardAttributes)));
}
catch (NamingException e) {
catch (Exception e) {
Log.error("Error populating groups from LDAP", e);
return Collections.emptyList();
}
......@@ -217,24 +223,24 @@ public class LdapGroupProvider implements GroupProvider {
return new ArrayList<Group>(groups);
}
public Collection<Group> getGroups(int start, int num) {
public Collection<Group> getGroups(int startIndex, int numResults) {
// Get an enumeration of all groups in the system
String searchFilter = MessageFormat.format(manager.getGroupSearchFilter(), "*");
NamingEnumeration<SearchResult> answer;
try {
answer = searchForGroups(searchFilter, standardAttributes);
}
catch (NamingException e) {
catch (Exception e) {
Log.error("Error searching for groups in LDAP", e);
return Collections.emptyList();
}
// Place all groups that are wanted into an enumeration
Vector<SearchResult> v = new Vector<SearchResult>();
for (int i = 1; answer.hasMoreElements() && i <= (start + num); i++) {
for (int i = 1; answer.hasMoreElements() && i <= (startIndex + numResults); i++) {
try {
SearchResult sr = answer.next();
if (i >= start) {
if (i >= startIndex) {
v.add(sr);
}
}
......@@ -277,7 +283,7 @@ public class LdapGroupProvider implements GroupProvider {
try {
return populateGroups(searchForGroups(filter, standardAttributes));
}
catch (NamingException e) {
catch (Exception e) {
Log.error("Error populating groups recieved from LDAP", e);
return Collections.emptyList();
}
......@@ -329,6 +335,148 @@ public class LdapGroupProvider implements GroupProvider {
return true;
}
public Collection<Group> 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));
}
// 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 new GroupCollection(groupNames.toArray(new String[groupNames.size()]));
}
public Collection<Group> search(String query, int startIndex, int numResults) {
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(")");
// TODO: used paged results is 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()) {
// 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;
}
}
// 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 new GroupCollection(groupNames.toArray(new String[groupNames.size()]));
}
public boolean isSearchSupported() {
return true;
}
/**
* An auxilary method used to perform LDAP queries based on a provided LDAP search filter.
*
......@@ -336,15 +484,20 @@ public class LdapGroupProvider implements GroupProvider {
* @return an enumeration of SearchResult.
*/
private NamingEnumeration<SearchResult> searchForGroups(String searchFilter,
String[] returningAttributes) throws NamingException
String[] returningAttributes) throws NamingException, IOException
{
if (manager.isDebugEnabled()) {
Log.debug("Trying to find all groups in the system.");
}
DirContext ctx = null;
LdapContext ctx = null;
NamingEnumeration<SearchResult> answer;
try {
ctx = manager.getContext();
// Sort on username field.
Control[] searchControl = new Control[]{
new SortControl(new String[]{manager.getGroupNameField()}, Control.NONCRITICAL)
};
ctx.setRequestControls(searchControl);
if (manager.isDebugEnabled()) {
Log.debug("Starting LDAP search...");
Log.debug("Using groupSearchFilter: " + searchFilter);
......
......@@ -432,7 +432,7 @@ public class LdapUserProvider implements UserProvider {
if (fields.size() > 1) {
filter.append(")");
}
// TODO: used paged results is supported by LDAP server.
// 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()) {
......
......@@ -10,12 +10,8 @@
--%>
<%@ page import="org.jivesoftware.util.*,
org.jivesoftware.wildfire.user.*,
java.util.*,
java.text.DateFormat,
org.jivesoftware.admin.*,
org.jivesoftware.wildfire.group.*,
java.net.URLDecoder,
java.net.URLEncoder"
%>
......@@ -41,8 +37,20 @@
webManager.setRowsPerPage("group-summary", range);
}
// Get the user manager
int groupCount = webManager.getGroupManager().getGroupCount();
Collection<Group> groups = webManager.getGroupManager().getGroups(start, range);
String search = null;
if (webManager.getGroupManager().isSearchSupported() && request.getParameter("search") != null
&& !request.getParameter("search").trim().equals(""))
{
search = request.getParameter("search");
// Use the search terms to get the list of groups and group count.
groups = webManager.getGroupManager().search(search, start, range);
// Get the count as a search for *all* groups. That will let us do pagination even
// though it's a bummer to execute the search twice.
groupCount = webManager.getGroupManager().search(search).size();
}
// paginator vars
int numPages = (int)Math.ceil((double)groupCount/(double)range);
......@@ -54,7 +62,7 @@
<div class="jive-success">
<table cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr><td class="jive-icon"><img src="images/success-16x16.gif" width="16" height="16" border="0"></td>
<tr><td class="jive-icon"><img src="images/success-16x16.gif" width="16" height="16" border="0" alt=""></td>
<td class="jive-icon-label">
<fmt:message key="group.summary.delete_group" />
</td></tr>
......@@ -64,14 +72,43 @@
<% } %>
<p>
<fmt:message key="group.summary.total_group" /> <b><%= webManager.getGroupManager().getGroupCount() %></b>
<% if (webManager.getGroupManager().isSearchSupported()) { %>
<form action="group-summary.jsp" method="get" name="searchForm">
<table border="0" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td valign="bottom">
<fmt:message key="group.summary.total_group" /> <b><%= groupCount %></b>
<% if (numPages > 1) { %>
, <fmt:message key="global.showing" /> <%= (start+1) %>-<%= (start+range) %>
<% } %>
</p>
</td>
<td align="right" valign="bottom">
<fmt:message key="group.summary.search" />: <input type="text" size="30" maxlength="150" name="search" value="<%= ((search!=null) ? search : "") %>">
</td>
</tr>
</table>
</form>
<script language="JavaScript" type="text/javascript">
document.searchForm.search.focus();
</script>
<% }
// Otherwise, searching is not supported.
else {
%>
<p>
<fmt:message key="group.summary.total_group" /> <b><%= groupCount %></b>
<% if (numPages > 1) { %>
, <fmt:message key="global.showing" /> <%= (start+1) %>-<%= (start+range) %>
<% } %>
</p>
<% } %>
<% if (numPages > 1) { %>
......@@ -82,7 +119,7 @@
String sep = ((i+1)<numPages) ? " " : "";
boolean isCurrent = (i+1) == curPage;
%>
<a href="group-summary.jsp?start=<%= (i*range) %>"
<a href="group-summary.jsp?start=<%= (i*range) %><%= search!=null? "&search=" + URLEncoder.encode(search, "UTF-8") : ""%>"
class="<%= ((isCurrent) ? "jive-current" : "") %>"
><%= (i+1) %></a><%= sep %>
......@@ -100,14 +137,16 @@
<th nowrap><fmt:message key="group.summary.page_name" /></th>
<th nowrap><fmt:message key="group.summary.page_member" /></th>
<th nowrap><fmt:message key="group.summary.page_admin" /></th>
<% // Only show edit and delete options if the groups aren't read-only.
if (!webManager.getGroupManager().isReadOnly()) { %>
<th nowrap><fmt:message key="group.summary.page_edit" /></th>
<th nowrap><fmt:message key="global.delete" /></th>
<% } %>
</tr>
</thead>
<tbody>
<% // Print the list of groups
Collection<Group> groups = webManager.getGroupManager().getGroups(start, range);
if (groups.isEmpty()) {
%>
<tr>
......@@ -142,16 +181,19 @@
<td width="10%" align="center">
<%= group.getAdmins().size() %>
</td>
<% // Only show edit and delete options if the groups aren't read-only.
if (!webManager.getGroupManager().isReadOnly()) { %>
<td width="1%" align="center">
<a href="group-edit.jsp?group=<%= groupName %>"
title=<fmt:message key="global.click_edit" />
><img src="images/edit-16x16.gif" width="17" height="17" border="0" alt=""></a>
><img src="images/edit-16x16.gif" width="16" height="16" border="0" alt=""></a>
</td>
<td width="1%" align="center" style="border-right:1px #ccc solid;">
<a href="group-delete.jsp?group=<%= groupName %>"
title=<fmt:message key="global.click_delete" />
><img src="images/delete-16x16.gif" width="16" height="16" border="0" alt=""></a>
</td>
<% } %>
</tr>
<%
......@@ -162,7 +204,7 @@
</div>
<% if (numPages > 1) { %>
<br>
<p>
<fmt:message key="global.pages" />
[
......@@ -170,7 +212,7 @@
String sep = ((i+1)<numPages) ? " " : "";
boolean isCurrent = (i+1) == curPage;
%>
<a href="group-summary.jsp?start=<%= (i*range) %>"
<a href="group-summary.jsp?start=<%= (i*range) %><%= search!=null? "&search=" + URLEncoder.encode(search, "UTF-8") : ""%>"
class="<%= ((isCurrent) ? "jive-current" : "") %>"
><%= (i+1) %></a><%= sep %>
......
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