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).
*/
......
......@@ -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);
......
......@@ -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