Commit 502f0b3b authored by Matt Tucker's avatar Matt Tucker Committed by matt

LDAP doc improvements, added sorting (JM-132).


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@894 b35dd754-fafc-0310-a699-88a17e54d16e
parent 46d7393b
......@@ -12,8 +12,8 @@
<h2>Introduction</h2>
<p>
This document details how to configure your Jive Messenger installation to use an external LDAP store when
authenticating users.
This document details how to configure your Jive Messenger installation to use
an external LDAP store when authenticating users and loading user profile information.
</p>
<h2>Background</h2>
<p>
......@@ -31,10 +31,10 @@ functionality and allows Jive Messenger to:
Use a LDAP server to authenticate a user's identity.
</li>
<li>
Load user profile information from a LDAP directory. Note that Jive Messenger treats the LDAP directory
as read-only.
Load user profile information from a LDAP directory.
</li>
</ul>
<b>Note:</b> Jive Messenger treats the LDAP directory as read-only.
</p>
<p>
This document will guide you through configuring LDAP support in Jive Messenger. These
......@@ -59,32 +59,44 @@ Restart Jive Messenger.
<h3>Editing the Config File</h3>
<p>
Open the configuration file <tt>conf/jive-messenger.xml</tt> from your Jive Messenger installation in your favorite
editor and add or change the following settings:
editor and add or change the following settings. Properties flagged with (<font color="red"><b>*</b></font>) must be set,
all other properties are optional:
</p>
<ul>
<li>provider.user.className -- set the value to "org.jivesoftware.messenger.ldap.LdapUserProvider".</li>
<li>provider.auth.className -- set the value to "org.jivesoftware.messenger.ldap.LdapAuthProvider".</li>
<li>ldap.host -- LDAP server host; e.g. localhost or machine.example.com, etc.</li>
<li>ldap.port -- LDAP server port number</li>
<li>ldap.usernameField -- the field name that the username lookups will be performed on. If this property is not set,
the default value is <tt>uid</tt></li>
<li>ldap.baseDN -- the starting DN that searches for users will performed with. The entire subtree
<li><font color="red"><b>*</b></font> provider.user.className -- set the value to "org.jivesoftware.messenger.ldap.LdapUserProvider".</li>
<li><font color="red"><b>*</b></font> provider.auth.className -- set the value to "org.jivesoftware.messenger.ldap.LdapAuthProvider".</li>
<li><font color="red"><b>*</b></font> ldap.host -- LDAP server host; e.g. localhost or machine.example.com, etc.</li>
<li>ldap.port -- LDAP server port number. If this property is not set, the default value is 389.</li>
<li><font color="red"><b>*</b></font> ldap.baseDN -- the starting DN that searches for users will performed with. The entire subtree
under the base DN will be searched for user accounts.
</li>
<li>ldap.nameField -- the field name that holds the user's name. If this property is not set, the default value is
<tt>cn</tt></li>
<li>ldap.emailField -- the field name that holds the user's email address. If this property is not set,
the default value is <tt>mail</tt>.</li>
<li>ldap.adminDN -- a directory administrator's DN. All directory operations will be performed
with this account. The admin must be able to perform searches and load user records. The user does
not need to be able to make changes to the directory, as Jive Messenger treats the directory as read-only.
If this property is not set, an anonymous login to the server will be attempted.
</li>
<li>ldap.adminPassword -- the password for the directory administrator.</li>
<li>ldap.usernameField -- the field name that the username lookups will be performed on. If this property is not set,
the default value is <tt>uid</tt></li>
<li>ldap.nameField -- the field name that holds the user's name. If this property is not set, the default value is
<tt>cn</tt></li>
<li>ldap.emailField -- the field name that holds the user's email address. If this property is not set,
the default value is <tt>mail</tt>.</li>
<li>ldap.debugEnabled -- a value of "true" if debugging should be turned on. When on, trace
information about buffers sent and received by the LDAP provider is written to System.out</li>
<li>ldap.sslEnabled -- a value of "true" to enable SSL connections to your LDAP server. If you
enable SSL connections, the LDAP server port number most likely should be changed to 636.
</li>
enable SSL connections, the LDAP server port number most likely should be changed to 636.</li>
<li>ldap.initialContextFactory -- the name of the class that should be used as an initial context
factory. if this value is not specified, "com.sun.jndi.ldap.LdapCtxFactory" will be used instead.
Most users will not need to set this value.
<li>ldap.autoFollowReferrals -- a value of "true" indicates that LDAP referrals should be automatically
followed. If this property is not set or is set to "false", the referral policy used is left up to
to the provider. A referral is an entity that is used to redirect a client's request to another server.
A referral contains the names and locations of other objects. It is sent by the server to indicate
that the information that the client has requested can be found at another location (or locations),
possibly at another server or several servers.
<li>ldap.connectionPoolEnabled -- a value of "false" disables LDAP connection pooling. If this
property is not set, the default value is "true".
</ul>
<p>
Below is a sample config file section:
......@@ -95,7 +107,7 @@ Below is a sample config file section:
&lt;ldap&gt;
&lt;host&gt;&lt;/host&gt;
&lt;port>389&lt;/port&gt;
<&lt;usernameField&gt;uid&lt;/usernameField&gt;
&lt;usernameField&gt;uid&lt;/usernameField&gt;
&lt;nameField&gt;cn&lt;/nameField&gt;
&lt;emailField&gt;mail&lt;/emailField&gt;
&lt;baseDN&gt;ou=People;dc=example;dc=com&lt;/baseDN&gt;
......@@ -114,7 +126,7 @@ Below is a sample config file section:
&lt;/jive&gt;
</code></pre>
<p>Finally, you'll most likely want to change which usernames are authorized to login to the
<p>You'll most likely want to change which usernames are authorized to login to the
admin console. By default, only the user with username "admin" is allowed to login. However,
you may have different users in your LDAP directory that you'd like to be administrators. The
list of authorized usernames is controlled via the <tt>adminConsole.authorizedUsernames</tt>
......@@ -130,5 +142,79 @@ property. For example, to let the usersnames "joe" and "jane" login to the admin
...
&lt;/jive&gt;
</code></pre>
<p><a name="debugging"><h2>Custom Inital Context Factory</h2></a></p>
<p>
Some LDAP servers or application servers may require that a different LDAP
initial context factory be used rather than the default (com.sun.jndi.ldap.LdapCtxFactory).
You can set a custom initial context factory by adding the following to jive_config.xml:
<pre>&lt;ldap&gt;
... other ldap settings here
&lt;initialContextFactory&gt;com.foo.factoryClass&lt;/initialContextFactory&gt;
&lt;/ldap&gt;</pre>
</p>
<p><a name="connectionPool"><h2>Connection Pooling</h2></a></p>
The default LDAP provider (Sun's) support pooling of connections to the LDAP
server. Connection pooling can greatly improve performance, especially on
systems with high load. Connection pooling is enabled by default, but can
be disabled by setting the Jive property <tt>ldap.connectionPoolEnabled</tt>
to <tt>false</tt>:
<pre>&lt;ldap&gt;
... other ldap settings here
&lt;connectionPoolEnabled&gt;false&lt;/connectionPoolEnabled&gt;
&lt;/ldap&gt;</pre></p>
<p>
You should set several Java system properties to change default pool settings.
For more information, see the following pages:
<ul>
<li> <a href="http://java.sun.com/products/jndi/tutorial/ldap/connect/pool.html">
http://java.sun.com/products/jndi/tutorial/ldap/connect/pool.html</a>
<li> <a href="http://java.sun.com/products/jndi/tutorial/ldap/connect/config.html">
http://java.sun.com/products/jndi/tutorial/ldap/connect/config.html</a>
</ul>
</p>
<p>Note that if you turn on LDAP debugging, connection pooling will not be enabled.
If SSL LDAP mode is enabled, you must set a system property to enable pooling of
SSL LDAP connections.</p>
<h2>LDAP FAQ</h2>
<p>
<b>Can I create new users through Jive Messenger when using LDAP?</b>
<ul>No, Jive Messenger treats LDAP directories as read-only. Therefore, it's
not possible to create or edit users through the application.</ul>
<b>Why is the list of usernames not sorted in the admin console when using LDAP?</b>
<ul>Several popular LDAP servers such as OpenLDAP do not support server-side
sorting of search results. On those servers, users will appear out of order.</ul>
<b>I switched to LDAP and now cannot login to the admin console. What happened?</b>
<ul>If you can no longer login to the admin console after switching, one of two
things most likely happened:<ol>
<li>By default, only the username "admin" is allowed to login to the
admin console. Your directory may not contain a user with a username
of "admin". In that case, you should modify the list of usernames authorized
to login to the admin console (see above).
<li>You may have set the baseDN to an incorrect value. The LDAP module
recursively searches for users under the node in the directory specified
by the baseDN. When the baseDN is incorrect, no users will be found.
</ol>
You can also enable debugging to get more information from the LDAP module. To
do this, add &lt;log&gt;&lt;debug&gt;&lt;enabled&gt;true&lt;/enabled&gt;&lt;/debug&gt;&lt;/log&gt;
to your <tt>conf/jive_messenger.xml</tt> file. Log statements will be written
to the <tt>logs/debug.log</tt> file.
</ul>
</body>
</html>
\ No newline at end of file
......@@ -18,21 +18,26 @@ import org.jivesoftware.util.Log;
import java.util.Hashtable;
import java.net.URLEncoder;
import javax.naming.*;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.directory.*;
/**
* Centralized administration of LDAP connections. The getInstance() method
* should be used to get an instace. The following configure this manager:<ul>
* <li> ldap.host
* <li> ldap.port
* <li> ldap.usernameField
* <li> ldap.baseDN
* <li> ldap.adminDN
* <li> ldap.adminPassword
* <li> ldap.ldapDebugEnabled
* <li> ldap.sslEnabled
* <li> ldap.initialContextFactory -- if this value is not specified,
* "com.sun.jndi.ldap.LdapCtxFactory" will be used instead.
* <li>ldap.host</li>
* <li>ldap.port</li>
* <li>ldap.baseDN</li>
* <li>ldap.adminDN</li>
* <li>ldap.adminPassword</li>
* <li>ldap.usernameField -- default value is "uid".</li>
* <li>ldap.nameField -- default value is "cn".</li>
* <li>ldap.emailField -- default value is "mail".</li>
* <li>ldap.ldapDebugEnabled</li>
* <li>ldap.sslEnabled</li>
* <li>ldap.autoFollowReferrals</li>
* <li>ldap.initialContextFactory -- if this value is not specified,
* "com.sun.jndi.ldap.LdapCtxFactory" will be used.</li>
* </ul>
*
* @author Matt Tucker
......@@ -51,6 +56,7 @@ public class LdapManager {
private boolean ldapDebugEnabled = false;
private boolean sslEnabled = false;
private String initialContextFactory;
private boolean followReferrals = false;
private boolean connectionPoolEnabled = true;
private static LdapManager instance = new LdapManager();
......@@ -98,8 +104,12 @@ public class LdapManager {
}
this.adminDN = JiveGlobals.getXMLProperty("ldap.adminDN");
this.adminPassword = JiveGlobals.getXMLProperty("ldap.adminPassword");
this.ldapDebugEnabled = "true".equals(JiveGlobals.getXMLProperty("ldap.ldapDebugEnabled"));
this.sslEnabled = "true".equals(JiveGlobals.getXMLProperty("ldap.sslEnabled"));
this.ldapDebugEnabled = Boolean.valueOf(JiveGlobals.getXMLProperty(
"ldap.ldapDebugEnabled")).booleanValue();
this.sslEnabled = Boolean.valueOf(JiveGlobals.getXMLProperty(
"ldap.sslEnabled")).booleanValue();
this.followReferrals = Boolean.valueOf(JiveGlobals.getXMLProperty(
"ldap.autoFollowReferrals")).booleanValue();
this.initialContextFactory = JiveGlobals.getXMLProperty("ldap.initialContextFactory");
if (initialContextFactory != null) {
try {
......@@ -131,6 +141,7 @@ public class LdapManager {
Log.debug("\t sslEnabled: " + sslEnabled);
Log.debug("\t initialContextFactory: " + initialContextFactory);
Log.debug("\t connectionPoolEnabled: " + connectionPoolEnabled);
Log.debug("\t autoFollowReferrals: " + followReferrals);
}
}
......@@ -142,7 +153,7 @@ public class LdapManager {
* @return a connection to the LDAP server.
* @throws NamingException if there is an error making the LDAP connection.
*/
public DirContext getContext() throws NamingException {
public LdapContext getContext() throws NamingException {
return getContext(baseDN);
}
......@@ -155,7 +166,7 @@ public class LdapManager {
* @return a connection to the LDAP server.
* @throws NamingException if there is an error making the LDAP connection.
*/
public DirContext getContext(String baseDN) throws NamingException {
public LdapContext getContext(String baseDN) throws NamingException {
boolean debug = Log.isDebugEnabled();
if (debug) {
Log.debug("Creating a DirContext in LdapManager.getContext()...");
......@@ -172,25 +183,33 @@ public class LdapManager {
}
// Use simple authentication to connect as the admin.
env.put(Context.SECURITY_AUTHENTICATION, "simple");
if (adminDN != null) {
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, adminDN);
if (adminPassword != null) {
env.put(Context.SECURITY_CREDENTIALS, adminPassword);
}
}
if (adminPassword != null) {
env.put(Context.SECURITY_CREDENTIALS, adminPassword);
// No login information so attempt to use anonymous login.
if (adminDN != null) {
env.put(Context.SECURITY_AUTHENTICATION, "none");
}
if (ldapDebugEnabled) {
env.put("com.sun.jndi.ldap.trace.ber", System.err);
}
if (connectionPoolEnabled) {
env.put("com.sun.jndi.ldap.connect.pool", "true");
}
if (followReferrals) {
env.put(Context.REFERRAL, "follow");
}
if (debug) {
Log.debug("Created hashtable with context values, attempting to create context...");
}
// Create new initial context
DirContext context = new InitialDirContext(env);
LdapContext context = new InitialLdapContext(env, null);
if (debug) {
Log.debug("... context created successfully, returning.");
}
......
......@@ -16,6 +16,9 @@ import org.jivesoftware.util.Log;
import javax.naming.directory.*;
import javax.naming.NamingEnumeration;
import javax.naming.ldap.Control;
import javax.naming.ldap.SortControl;
import javax.naming.ldap.LdapContext;
import java.util.Collection;
import java.util.Date;
import java.util.List;
......@@ -105,9 +108,15 @@ public class LdapUserProvider implements UserProvider {
public Collection<User> getUsers() {
List<String> usernames = new ArrayList<String>();
DirContext ctx = null;
LdapContext ctx = null;
try {
ctx = manager.getContext();
// 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 constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
......@@ -127,7 +136,12 @@ public class LdapUserProvider implements UserProvider {
Log.error(e);
}
finally {
try { ctx.close(); }
try {
if (ctx != null) {
ctx.setRequestControls(null);
ctx.close();
}
}
catch (Exception ignored) { }
}
return new UserCollection((String[])usernames.toArray(new String[usernames.size()]));
......@@ -135,9 +149,15 @@ public class LdapUserProvider implements UserProvider {
public Collection<User> getUsers(int startIndex, int numResults) {
List<String> usernames = new ArrayList<String>();
DirContext ctx = null;
LdapContext ctx = null;
try {
ctx = manager.getContext();
// 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 constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
......@@ -165,7 +185,12 @@ public class LdapUserProvider implements UserProvider {
Log.error(e);
}
finally {
try { ctx.close(); }
try {
if (ctx != null) {
ctx.setRequestControls(null);
ctx.close();
}
}
catch (Exception ignored) { }
}
return new UserCollection((String[])usernames.toArray(new String[usernames.size()]));
......
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