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

Code cleanup.


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@234 b35dd754-fafc-0310-a699-88a17e54d16e
parent 1ce53617
...@@ -13,39 +13,37 @@ package org.jivesoftware.messenger.ldap; ...@@ -13,39 +13,37 @@ package org.jivesoftware.messenger.ldap;
import org.jivesoftware.messenger.JiveGlobals; import org.jivesoftware.messenger.JiveGlobals;
import org.jivesoftware.messenger.auth.UnauthorizedException; import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.util.Log;
import java.util.Hashtable; import java.util.Hashtable;
import javax.naming.Context; import java.net.URLEncoder;
import javax.naming.NamingEnumeration; import javax.naming.*;
import javax.naming.NamingException; import javax.naming.directory.*;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
/** /**
* Centralized administration of LDAP connections. The getInstance() method * Centralized administration of LDAP connections. The getInstance() method
* should be used to get an instace. The following Jive properties correspond * should be used to get an instace. The following Jive properties correspond
* to the properties of this manager: <ul> * to the properties of this manager: <ul>
* <li> ldap.host * <li> ldap.host
* <li> ldap.port * <li> ldap.port
* <li> ldap.usernameField * <li> ldap.usernameField
* <li> ldap.baseDN * <li> ldap.baseDN
* <li> ldap.adminDN * <li> ldap.adminDN
* <li> ldap.adminPassword * <li> ldap.adminPassword
* <li> ldap.mode * <li> ldap.mode
* <li> ldap.debugEnabled * <li> ldap.ldapDebugEnabled
* <li> ldap.sslEnabled * <li> ldap.sslEnabled
* <li> ldap.initialContextFactory -- if this value is not specified,
* "com.sun.jndi.ldap.LdapCtxFactory" will be used instead.
* </ul><p> * </ul><p>
* <p/> *
* The LDAP module operates in one of two modes:<ul> * The LDAP module operates in one of two modes:<ul>
* <li> ALL_LDAP_MODE -- all user data is stored in LDAP, including Jive-specific data such * <li> ALL_LDAP_MODE -- all user data is stored in LDAP, including Jive-specific data such
* as the number of reward points, etc. This option requires making modifications to * as the number of reward points, etc. This option requires making modifications to
* the schema of the directory. * the schema of the directory.
* <li> LDAP_DB_MODE -- only critical user data is stored in LDAP (username, name, and email). * <li> LDAP_DB_MODE -- only critical user data is stored in LDAP (username, name, and email).
* All Jive-specific data is stored in the normal jiveUser and userProperty database * All Jive-specific data is stored in the normal jiveUser and userProperty database
* tables. This mode requires no changes to the LDAP directory.</ul> * tables. This mode requires no changes to the LDAP directory.</ul>
* <p/>
* LDAP connections are made via JNDI using the Sun LDAP provider.
* *
* @author Matt Tucker * @author Matt Tucker
*/ */
...@@ -68,11 +66,14 @@ public class LdapManager { ...@@ -68,11 +66,14 @@ public class LdapManager {
private String nameField = "cn"; private String nameField = "cn";
private String emailField = "mail"; private String emailField = "mail";
private String baseDN = ""; private String baseDN = "";
private String alternateBaseDN = null;
private String adminDN; private String adminDN;
private String adminPassword; private String adminPassword;
private int mode = 0; private int mode = 0;
private boolean debugEnabled = false; private boolean ldapDebugEnabled = false;
private boolean sslEnabled = false; private boolean sslEnabled = false;
private String initialContextFactory;
private boolean connectionPoolEnabled = true;
private static LdapManager instance = new LdapManager(); private static LdapManager instance = new LdapManager();
...@@ -96,8 +97,7 @@ public class LdapManager { ...@@ -96,8 +97,7 @@ public class LdapManager {
try { try {
this.port = Integer.parseInt(portStr); this.port = Integer.parseInt(portStr);
} }
catch (NumberFormatException nfe) { catch (NumberFormatException nfe) { }
}
} }
if (JiveGlobals.getXMLProperty("ldap.usernameField") != null) { if (JiveGlobals.getXMLProperty("ldap.usernameField") != null) {
this.usernameField = JiveGlobals.getXMLProperty("ldap.usernameField"); this.usernameField = JiveGlobals.getXMLProperty("ldap.usernameField");
...@@ -105,45 +105,101 @@ public class LdapManager { ...@@ -105,45 +105,101 @@ public class LdapManager {
if (JiveGlobals.getXMLProperty("ldap.baseDN") != null) { if (JiveGlobals.getXMLProperty("ldap.baseDN") != null) {
this.baseDN = JiveGlobals.getXMLProperty("ldap.baseDN"); this.baseDN = JiveGlobals.getXMLProperty("ldap.baseDN");
} }
if (JiveGlobals.getXMLProperty("ldap.alternateBaseDN") != null) {
this.alternateBaseDN = JiveGlobals.getXMLProperty("ldap.alternateBaseDN");
}
if (JiveGlobals.getXMLProperty("ldap.nameField") != null) { if (JiveGlobals.getXMLProperty("ldap.nameField") != null) {
this.nameField = JiveGlobals.getXMLProperty("ldap.nameField"); this.nameField = JiveGlobals.getXMLProperty("ldap.nameField");
} }
if (JiveGlobals.getXMLProperty("ldap.emailField") != null) { if (JiveGlobals.getXMLProperty("ldap.emailField") != null) {
this.emailField = JiveGlobals.getXMLProperty("ldap.emailField"); this.emailField = JiveGlobals.getXMLProperty("ldap.emailField");
} }
if (JiveGlobals.getXMLProperty("ldap.connectionPoolEnabled") != null) {
this.connectionPoolEnabled = Boolean.valueOf(
JiveGlobals.getXMLProperty("ldap.connectionPoolEnabled")).booleanValue();
}
this.adminDN = JiveGlobals.getXMLProperty("ldap.adminDN"); this.adminDN = JiveGlobals.getXMLProperty("ldap.adminDN");
this.adminPassword = JiveGlobals.getXMLProperty("ldap.adminPassword"); this.adminPassword = JiveGlobals.getXMLProperty("ldap.adminPassword");
this.debugEnabled = "true".equals(JiveGlobals.getXMLProperty("ldap.debugEnabled")); this.ldapDebugEnabled = "true".equals(JiveGlobals.getXMLProperty("ldap.ldapDebugEnabled"));
this.sslEnabled = "true".equals(JiveGlobals.getXMLProperty("ldap.sslEnabled")); this.sslEnabled = "true".equals(JiveGlobals.getXMLProperty("ldap.sslEnabled"));
String modeStr = JiveGlobals.getXMLProperty("ldap.mode"); String modeStr = JiveGlobals.getXMLProperty("ldap.mode");
if (modeStr != null) { if (modeStr != null) {
try { try {
this.mode = Integer.parseInt(modeStr); this.mode = Integer.parseInt(modeStr);
} }
catch (NumberFormatException nfe) { catch (NumberFormatException nfe) { }
}
this.initialContextFactory = JiveGlobals.getXMLProperty("ldap.initialContextFactory");
if (initialContextFactory != null) {
try {
Class.forName(initialContextFactory);
}
catch (ClassNotFoundException cnfe) {
Log.error("Initial context factory class failed to load: " + initialContextFactory +
". Using default initial context factory class instead.");
initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
} }
} }
// Use default value if none was set.
else {
initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
}
if (Log.isDebugEnabled()) {
Log.debug("Created new LdapManager() instance, fields:");
Log.debug("\t host: " + host);
Log.debug("\t port: " + port);
Log.debug("\t usernamefield: " + usernameField);
Log.debug("\t baseDN: " + baseDN);
Log.debug("\t alternateBaseDN: " + alternateBaseDN);
Log.debug("\t nameField: " + nameField);
Log.debug("\t emailField: " + emailField);
Log.debug("\t adminDN: " + adminDN);
Log.debug("\t adminPassword: " + adminPassword);
Log.debug("\t ldapDebugEnabled: " + ldapDebugEnabled);
Log.debug("\t sslEnabled: " + sslEnabled);
Log.debug("\t mode: " + mode);
Log.debug("\t initialContextFactory: " + initialContextFactory);
Log.debug("\t connectionPoolEnabled: " + connectionPoolEnabled);
}
} }
/** /**
* Returns a DirContext for the LDAP server that can be used to perform * Returns a DirContext for the LDAP server that can be used to perform
* lookups and searches. The context uses the admin login that is defined * lookups and searches using the default base DN. The context uses the
* by <tt>adminDN</tt> and <tt>adminPassword</tt>. * admin login that is defined by <tt>adminDN</tt> and <tt>adminPassword</tt>.
* *
* @return a connection to the LDAP server. * @return a connection to the LDAP server.
* @throws NamingException if there is an error making the LDAP connection. * @throws NamingException if there is an error making the LDAP connection.
*/ */
public DirContext getContext() throws NamingException { public DirContext getContext() throws NamingException {
// Set up the environment for creating the initial context return getContext(baseDN);
}
/**
* Returns a DirContext for the LDAP server that can be used to perform
* lookups and searches using the specified base DN. The context uses the
* admin login that is defined by <tt>adminDN</tt> and <tt>adminPassword</tt>.
*
* @param baseDN the base DN to use for the context.
* @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 {
boolean debug = Log.isDebugEnabled();
if (debug) {
Log.debug("Creating a DirContext in LdapManager.getContext()...");
}
// Set up the environment for creating the initial context
Hashtable env = new Hashtable(); Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
"com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, getProviderURL(baseDN));
if (sslEnabled) { if (sslEnabled) {
env.put("java.naming.ldap.factory.socket", "org.jivesoftware.util.ssl.DummySSLSocketFactory"); env.put("java.naming.ldap.factory.socket",
"com.jivesoftware.util.ssl.DummySSLSocketFactory");
env.put(Context.SECURITY_PROTOCOL, "ssl"); env.put(Context.SECURITY_PROTOCOL, "ssl");
} }
env.put(Context.PROVIDER_URL, "ldap://" + host + ":" +
port + "/" + baseDN);
// Use simple authentication to connect as the admin. // Use simple authentication to connect as the admin.
env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_AUTHENTICATION, "simple");
...@@ -153,52 +209,106 @@ public class LdapManager { ...@@ -153,52 +209,106 @@ public class LdapManager {
if (adminPassword != null) { if (adminPassword != null) {
env.put(Context.SECURITY_CREDENTIALS, adminPassword); env.put(Context.SECURITY_CREDENTIALS, adminPassword);
} }
if (debugEnabled) { if (ldapDebugEnabled) {
env.put("com.sun.jndi.ldap.trace.ber", System.err); env.put("com.sun.jndi.ldap.trace.ber", System.err);
} }
if (connectionPoolEnabled) {
env.put("com.sun.jndi.ldap.connect.pool", "true");
}
if (debug) {
Log.debug("Created hashtable with context values, attempting to create context...");
}
// Create new initial context // Create new initial context
return new InitialDirContext(env); DirContext context = new InitialDirContext(env);
if (debug) {
Log.debug("... context created successfully, returning.");
}
return context;
} }
/** /**
* Returns true if the user is able to successfully authenticate against * Returns true if the user is able to successfully authenticate against
* the LDAP server. The "simple" authentication protocol is used. * the LDAP server. The "simple" authentication protocol is used.
* *
* @param userDN the user's dn to authenticate (relative to <tt>baseDN</tt>). * @param userDN the user's dn to authenticate (relative to <tt>baseDN</tt>).
* @param password the user's password. * @param password the user's password.
* @return true if the user successfully authenticates. * @return true if the user successfully authenticates.
*/ */
public boolean checkAuthentication(String userDN, String password) { public boolean checkAuthentication(String userDN, String password) {
boolean debug = Log.isDebugEnabled();
if (debug) {
Log.debug("In LdapManager.checkAuthentication(userDN, password), userDN is: " + userDN + "...");
}
DirContext ctx = null; DirContext ctx = null;
try { try {
// See if the user authenticates. // See if the user authenticates.
Hashtable env = new Hashtable(); Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
env.put(Context.PROVIDER_URL, getProviderURL(baseDN));
if (sslEnabled) { if (sslEnabled) {
env.put("java.naming.ldap.factory.socket", "org.jivesoftware.util.ssl.DummySSLSocketFactory"); env.put("java.naming.ldap.factory.socket", "com.jivesoftware.util.ssl.DummySSLSocketFactory");
env.put(Context.SECURITY_PROTOCOL, "ssl"); env.put(Context.SECURITY_PROTOCOL, "ssl");
} }
env.put(Context.PROVIDER_URL, "ldap://" + host + ":" +
port + "/" + baseDN);
env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, userDN + "," + baseDN); env.put(Context.SECURITY_PRINCIPAL, userDN + "," + baseDN);
env.put(Context.SECURITY_CREDENTIALS, password); env.put(Context.SECURITY_CREDENTIALS, password);
if (debugEnabled) { if (ldapDebugEnabled) {
env.put("com.sun.jndi.ldap.trace.ber", System.err); env.put("com.sun.jndi.ldap.trace.ber", System.err);
} }
if (debug) {
Log.debug("Created context values, attempting to create context...");
}
ctx = new InitialDirContext(env); ctx = new InitialDirContext(env);
if (debug) {
Log.debug("... context created successfully, returning.");
}
} }
catch (NamingException ne) { catch (NamingException ne) {
return false; // If an alt baseDN is defined, attempt a lookup there.
} if (alternateBaseDN != null) {
finally { try { ctx.close(); }
try { catch (Exception ignored) { }
ctx.close(); try {
// See if the user authenticates.
Hashtable env = new Hashtable();
// Use a custom initial context factory if specified. Otherwise, use the default.
env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
env.put(Context.PROVIDER_URL, getProviderURL(alternateBaseDN));
if (sslEnabled) {
env.put("java.naming.ldap.factory.socket", "com.jivesoftware.util.ssl.DummySSLSocketFactory");
env.put(Context.SECURITY_PROTOCOL, "ssl");
}
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, userDN + "," + alternateBaseDN);
env.put(Context.SECURITY_CREDENTIALS, password);
if (ldapDebugEnabled) {
env.put("com.sun.jndi.ldap.trace.ber", System.err);
}
if (debug) {
Log.debug("Created context values, attempting to create context...");
}
ctx = new InitialDirContext(env);
}
catch (NamingException e) {
if (debug) {
Log.debug("Caught a naming exception when creating InitialContext", ne);
}
return false;
}
} }
catch (Exception e) { else {
if (debug) {
Log.debug("Caught a naming exception when creating InitialContext", ne);
}
return false;
} }
} }
finally {
try { ctx.close(); }
catch (Exception ignored) { }
}
return true; return true;
} }
...@@ -206,7 +316,45 @@ public class LdapManager { ...@@ -206,7 +316,45 @@ public class LdapManager {
* Finds a user's dn using their username. Normally, this search will * Finds a user's dn using their username. Normally, this search will
* be performed using the field "uid", but this can be changed by setting * be performed using the field "uid", but this can be changed by setting
* the <tt>usernameField</tt> property.<p> * the <tt>usernameField</tt> property.<p>
* <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 "mtucker", then we might
* find a userDN of "uid=mtucker,ou=People". 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 "uid" 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 uid: "uid=mtucker,ou=People" and
* "uid=mtucker,ou=Administrators". In such a case, it's not possible to
* uniquely identify a user, so this method will throw an error.<p>
*
* The dn that's returned is relative to the default <tt>baseDN</tt>.
*
* @param username the username to lookup the dn for.
* @return the dn associated with <tt>username</tt>.
* @throws Exception if the search for the dn fails.
*/
public String findUserDN(String username) throws Exception {
try {
return findUserDN(username, baseDN);
}
catch (Exception e) {
if (alternateBaseDN != null) {
return findUserDN(username, alternateBaseDN);
}
else {
throw e;
}
}
}
/**
* Finds a user's dn using their username in the specified baseDN. Normally, this search
* will be performed using the field "uid", but this can be changed by setting
* the <tt>usernameField</tt> property.<p>
*
* Searches are performed over all subtrees relative to the <tt>baseDN</tt>. * Searches are performed over all subtrees relative to the <tt>baseDN</tt>.
* For example, if the <tt>baseDN</tt> is "o=jivesoftware, o=com" and we * For example, if the <tt>baseDN</tt> is "o=jivesoftware, o=com" and we
* do a search for "mtucker", then we might find a userDN of * do a search for "mtucker", then we might find a userDN of
...@@ -218,28 +366,46 @@ public class LdapManager { ...@@ -218,28 +366,46 @@ public class LdapManager {
* in your LDAP directory with the same uid: "uid=mtucker,ou=People" and * in your LDAP directory with the same uid: "uid=mtucker,ou=People" and
* "uid=mtucker,ou=Administrators". In such a case, it's not possible to * "uid=mtucker,ou=Administrators". In such a case, it's not possible to
* uniquely identify a user, so this method will throw an error.<p> * uniquely identify a user, so this method will throw an error.<p>
* <p/> *
* The dn that's returned is relative to the <tt>baseDN</tt>. * The dn that's returned is relative to the <tt>baseDN</tt>.
* *
* @param username the username to lookup the dn for. * @param username the username to lookup the dn for.
* @param baseDN the base DN to use for this search.
* @return the dn associated with <tt>username</tt>. * @return the dn associated with <tt>username</tt>.
* @throws Exception if the search for the dn fails. * @throws Exception if the search for the dn fails.
* @see #findUserDN(String) to search using the default baseDN and alternateBaseDN.
*/ */
public String findUserDN(String username) throws Exception { public String findUserDN(String username, String baseDN) throws Exception {
boolean debug = Log.isDebugEnabled();
if (debug) {
Log.debug("Trying to find a user's DN based on their username. " + usernameField + ": " + username
+ ", Base DN: " + baseDN + "...");
}
DirContext ctx = null; DirContext ctx = null;
try { try {
ctx = getContext(); ctx = getContext(baseDN);
if (debug) {
Log.debug("Starting LDAP search...");
}
// Search for the dn based on the username. // Search for the dn based on the username.
SearchControls constraints = new SearchControls(); SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setReturningAttributes(new String[]{usernameField}); constraints.setReturningAttributes(new String[] { usernameField });
StringBuffer filter = new StringBuffer(); StringBuffer filter = new StringBuffer();
filter.append("(").append(usernameField).append("="); filter.append("(").append(usernameField).append("=");
filter.append(username).append(")"); filter.append(username).append(")");
NamingEnumeration answer = ctx.search("", filter.toString(), constraints); NamingEnumeration answer = ctx.search("", filter.toString(), constraints);
if (debug) {
Log.debug("... search finished");
}
if (answer == null || !answer.hasMoreElements()) { if (answer == null || !answer.hasMoreElements()) {
if (debug) {
Log.debug("User DN based on username '" + username + "' not found.");
}
throw new UnauthorizedException("Username " + username + " not found"); throw new UnauthorizedException("Username " + username + " not found");
} }
String userDN = ((SearchResult)answer.next()).getName(); String userDN = ((SearchResult)answer.next()).getName();
...@@ -249,20 +415,50 @@ public class LdapManager { ...@@ -249,20 +415,50 @@ public class LdapManager {
// There really isn't a way to handle this, so throw an exception. // 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. // The baseDN must be set correctly so that this doesn't happen.
if (answer.hasMoreElements()) { if (answer.hasMoreElements()) {
throw new Exception("LDAP username lookup for " + username + if (debug) {
Log.debug("Search for userDN based on username '" + username + "' found multiple " +
"responses, throwing exception.");
}
throw new Exception ("LDAP username lookup for " + username +
" matched multiple entries."); " matched multiple entries.");
} }
return userDN; return userDN;
} }
finally { catch (Exception e) {
try { if (debug) {
ctx.close(); Log.debug("Exception thrown when searching for userDN based on username '" + username + "'", e);
}
catch (Exception e) {
} }
throw e;
}
finally {
try { ctx.close(); }
catch (Exception ignored) { }
} }
} }
/**
* Returns a properly encoded URL for use as the PROVIDER_URL.
* If the encoding fails then the URL will contain the raw base dn.
*
* @param baseDN the base dn to use in the URL.
* @return the properly encoded URL for use in as PROVIDER_URL.
*/
private String getProviderURL(String baseDN) {
String ldapURL = "";
try {
// Create a correctly-encoded ldap URL for the PROVIDER_URL
ldapURL = "ldap://" + host + ":" + port + "/" +
URLEncoder.encode(baseDN, "UTF-8");
// The java.net.URLEncoder class encodes spaces as +, but they need to be %20
ldapURL = ldapURL.replaceAll("\\+", "%20");
}
catch (java.io.UnsupportedEncodingException e) {
// UTF-8 is not supported, fall back to using raw baseDN
ldapURL = "ldap://" + host + ":" + port + "/" + baseDN;
}
return ldapURL;
}
/** /**
* Returns the LDAP server host; e.g. <tt>localhost</tt> or * Returns the LDAP server host; e.g. <tt>localhost</tt> or
* <tt>machine.example.com</tt>, etc. This value is stored as the Jive * <tt>machine.example.com</tt>, etc. This value is stored as the Jive
...@@ -304,7 +500,7 @@ public class LdapManager { ...@@ -304,7 +500,7 @@ public class LdapManager {
*/ */
public void setPort(int port) { public void setPort(int port) {
this.port = port; this.port = port;
JiveGlobals.setXMLProperty("ldap.port", "" + port); JiveGlobals.setXMLProperty("ldap.port", ""+port);
} }
/** /**
...@@ -315,7 +511,7 @@ public class LdapManager { ...@@ -315,7 +511,7 @@ public class LdapManager {
* @return true if LDAP debugging is turned on. * @return true if LDAP debugging is turned on.
*/ */
public boolean isDebugEnabled() { public boolean isDebugEnabled() {
return debugEnabled; return ldapDebugEnabled;
} }
/** /**
...@@ -326,8 +522,8 @@ public class LdapManager { ...@@ -326,8 +522,8 @@ public class LdapManager {
* @param debugEnabled true if debugging should be turned on. * @param debugEnabled true if debugging should be turned on.
*/ */
public void setDebugEnabled(boolean debugEnabled) { public void setDebugEnabled(boolean debugEnabled) {
this.debugEnabled = debugEnabled; this.ldapDebugEnabled = debugEnabled;
JiveGlobals.setXMLProperty("ldap.debuggingEnabled", "" + debugEnabled); JiveGlobals.setXMLProperty("ldap.ldapDebugEnabled", ""+debugEnabled);
} }
/** /**
...@@ -346,6 +542,7 @@ public class LdapManager { ...@@ -346,6 +542,7 @@ public class LdapManager {
*/ */
public void setSslEnabled(boolean sslEnabled) { public void setSslEnabled(boolean sslEnabled) {
this.sslEnabled = sslEnabled; this.sslEnabled = sslEnabled;
JiveGlobals.setXMLProperty("ldap.sslEnabled", ""+sslEnabled);
} }
/** /**
...@@ -363,7 +560,7 @@ public class LdapManager { ...@@ -363,7 +560,7 @@ public class LdapManager {
* By default this is "uid". * By default this is "uid".
* *
* @param usernameField the LDAP field that the username lookup will be * @param usernameField the LDAP field that the username lookup will be
* performed on. * performed on.
*/ */
public void setUsernameField(String usernameField) { public void setUsernameField(String usernameField) {
this.usernameField = usernameField; this.usernameField = usernameField;
...@@ -406,7 +603,7 @@ public class LdapManager { ...@@ -406,7 +603,7 @@ public class LdapManager {
* By default this is "mail". * By default this is "mail".
* *
* @return the LDAP field that that correspond's to the user's email * @return the LDAP field that that correspond's to the user's email
* address. * address.
*/ */
public String getEmailField() { public String getEmailField() {
return emailField; return emailField;
...@@ -417,7 +614,7 @@ public class LdapManager { ...@@ -417,7 +614,7 @@ public class LdapManager {
* By default this is "mail". * By default this is "mail".
* *
* @param emailField the LDAP field that that correspond's to the user's * @param emailField the LDAP field that that correspond's to the user's
* email address. * email address.
*/ */
public void setEmailField(String emailField) { public void setEmailField(String emailField) {
this.emailField = emailField; this.emailField = emailField;
...@@ -450,6 +647,35 @@ public class LdapManager { ...@@ -450,6 +647,35 @@ public class LdapManager {
JiveGlobals.setXMLProperty("ldap.baseDN", baseDN); JiveGlobals.setXMLProperty("ldap.baseDN", baseDN);
} }
/**
* Returns the alternate starting DN that searches for users will performed with.
* Searches will performed on the entire sub-tree under the alternate base DN after
* they are performed on the main base DN.
*
* @return the alternate starting DN used for performing searches. If no alternate
* DN is set, this method will return <tt>null</tt>.
*/
public String getAlternateBaseDN() {
return alternateBaseDN;
}
/**
* Sets the alternate starting DN that searches for users will performed with.
* Searches will performed on the entire sub-tree under the alternate base DN after
* they are performed on the main base dn.
*
* @param alternateBaseDN the alternate starting DN used for performing searches.
*/
public void setAlternateBaseDN(String alternateBaseDN) {
this.alternateBaseDN = alternateBaseDN;
if (alternateBaseDN == null) {
JiveGlobals.deleteXMLProperty("ldap.alternateBaseDN");
}
else {
JiveGlobals.setXMLProperty("ldap.alternateBaseDN", alternateBaseDN);
}
}
/** /**
* Returns the starting admin DN that searches for admins will performed with. * Returns the starting admin DN that searches for admins will performed with.
* Searches will performed on the entire sub-tree under the admin DN. * Searches will performed on the entire sub-tree under the admin DN.
...@@ -485,7 +711,7 @@ public class LdapManager { ...@@ -485,7 +711,7 @@ public class LdapManager {
* Sets the admin password for the LDAP server we're connecting to. * Sets the admin password for the LDAP server we're connecting to.
* *
* @param adminPassword the admin password for the LDAP server we're * @param adminPassword the admin password for the LDAP server we're
* connecting to. * connecting to.
*/ */
public void setAdminPassword(String adminPassword) { public void setAdminPassword(String adminPassword) {
this.adminPassword = adminPassword; this.adminPassword = adminPassword;
...@@ -514,6 +740,6 @@ public class LdapManager { ...@@ -514,6 +740,6 @@ public class LdapManager {
*/ */
public void setMode(int mode) { public void setMode(int mode) {
this.mode = mode; this.mode = mode;
JiveGlobals.setXMLProperty("ldap.mode", "" + mode); JiveGlobals.setXMLProperty("ldap.mode", ""+mode);
} }
} }
\ No newline at end of file
...@@ -20,7 +20,6 @@ import org.jivesoftware.util.LongList; ...@@ -20,7 +20,6 @@ import org.jivesoftware.util.LongList;
import org.jivesoftware.messenger.user.UserIDProvider; import org.jivesoftware.messenger.user.UserIDProvider;
import org.jivesoftware.messenger.user.UserNotFoundException; import org.jivesoftware.messenger.user.UserNotFoundException;
import org.jivesoftware.messenger.user.spi.DbUserIDProvider; import org.jivesoftware.messenger.user.spi.DbUserIDProvider;
import org.jivesoftware.database.DbConnectionManager;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
...@@ -34,85 +33,48 @@ import javax.naming.directory.SearchControls; ...@@ -34,85 +33,48 @@ import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult; import javax.naming.directory.SearchResult;
/** /**
* <p>Ldap implementation of the UserIDProvider interface.</p> * LDAP implementation of the UserIDProvider interface.<p>
* <p>The LdapUserIDProvider can operate in two modes -- in the pure LDAP mode, all user data is stored in the LDAP *
* store. This mode generally requires modifications to the LDAP schema to accommodate data that Messenger needs.</p> * The LdapUserIDProvider can operate in two modes -- in the pure LDAP mode,
* <p>In the mixed mode, data that Messenger needs is stored locally.</p> * all user data is stored in the LDAP store. This mode generally requires
* * @author Jim Berrettini * modifications to the LDAP schema to accommodate data that Messenger needs.
* In the mixed mode, data that Messenger needs is stored locally.
*
* @author Jim Berrettini
*/ */
public class LdapUserIDProvider implements UserIDProvider { public class LdapUserIDProvider implements UserIDProvider {
private LdapManager manager; private LdapManager manager;
/** /**
* <p>Object type for users.</p> * Object type for users.
*/ */
public static final int USER_TYPE = 0; public static final int USER_TYPE = 0;
/** /**
* <p>Object type for chatbots.</p> * Object type for chatbots.
*/ */
public static final int CHATBOT_TYPE = 1; public static final int CHATBOT_TYPE = 1;
/** /**
* <p>The default domain id - the messenger domain.</p> * The default domain id - the messenger domain.
*/ */
public static final long DEFAULT_DOMAIN = 1; public static final long DEFAULT_DOMAIN = 1;
private static final String GET_USERID = "SELECT objectID FROM jiveUserID WHERE username=? AND domainID=? AND objectType=?"; private static final String GET_USERID =
private static final String GET_USERNAME = "SELECT username FROM jiveUserID WHERE objectID=? AND domainID=? AND objectType=?"; "SELECT objectID FROM jiveUserID WHERE username=? AND domainID=? AND objectType=?";
private static final String USER_COUNT = "SELECT count(*) FROM jiveUser"; private static final String GET_USERNAME =
private static final String INSERT_USERID = "INSERT INTO jiveUserID (username,domainID,objectType,objectID) VALUES (?,?,?,?)"; "SELECT username FROM jiveUserID WHERE objectID=? AND domainID=? AND objectType=?";
private static final String ALL_USERS = "SELECT userID from jiveUser"; private static final String USER_COUNT =
"SELECT count(*) FROM jiveUser";
private static final String INSERT_USERID =
"INSERT INTO jiveUserID (username,domainID,objectType,objectID) VALUES (?,?,?,?)";
private static final String ALL_USERS =
"SELECT userID from jiveUser";
public LdapUserIDProvider() { public LdapUserIDProvider() {
manager = LdapManager.getInstance(); manager = LdapManager.getInstance();
} }
/**
* <p>Obtain the user's username from their ID.</p>
*
* @param id the userID of the user
* @return the name of the user with the given userID
* @throws UserNotFoundException if no such user exists
*/
public String getUsername(long id) throws UserNotFoundException { public String getUsername(long id) throws UserNotFoundException {
if (manager.getMode() == LdapManager.ALL_LDAP_MODE) { if (manager.getMode() == LdapManager.ALL_LDAP_MODE) {
// Find userDN.
DirContext ctx = null;
try {
ctx = manager.getContext();
// Search for the dn based on the username.
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
constraints.setReturningAttributes(new String[]{"jiveUserID"});
StringBuffer filter = new StringBuffer();
filter.append("(").append("jiveUserID").append("=");
filter.append(id).append(")");
NamingEnumeration answer = ctx.search("", filter.toString(), constraints);
if (answer == null || !answer.hasMoreElements()) {
throw new UserNotFoundException("User not found: " + id);
}
String userDN = ((SearchResult)answer.next()).getName();
// Make sure there are no more search results. If there are, then
// the userID 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()) {
throw new UserNotFoundException("LDAP username lookup matched multiple entries.");
}
}
catch (Exception e) {
throw new UserNotFoundException(e);
}
finally {
try {
ctx.close();
}
catch (Exception e) {
}
}
return getUsernameFromLdap(id); return getUsernameFromLdap(id);
} }
else { else {
...@@ -120,34 +82,22 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -120,34 +82,22 @@ public class LdapUserIDProvider implements UserIDProvider {
} }
} }
/**
* <p>Obtain the user's username from their ID.</p>
*
* @param username the username to look up
* @return the userID corrresponding to the given username
* @throws UserNotFoundException
*/
public long getUserID(String username) throws UserNotFoundException { public long getUserID(String username) throws UserNotFoundException {
if (manager.getMode() == LdapManager.ALL_LDAP_MODE) { if (manager.getMode() == LdapManager.ALL_LDAP_MODE) {
return getUserIDFromLdap(username); return getUserIDFromLdap(username);
} }
else {
long id = 0L; long id = 0L;
try { try {
id = getUserIDLocally(username); id = getUserIDLocally(username);
} }
catch (UserNotFoundException e) { catch (UserNotFoundException e) {
id = generateNewUserIDLocally(username); id = generateNewUserIDLocally(username);
}
return id;
} }
return id;
} }
/**
* <p>Obtain the total number of users on the system.</p>
*
* @return total number of users on the system.
*/
public int getUserCount() { public int getUserCount() {
int count = 0; int count = 0;
// If using the pure LDAP mode. // If using the pure LDAP mode.
...@@ -173,18 +123,14 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -173,18 +123,14 @@ public class LdapUserIDProvider implements UserIDProvider {
Log.error(e); Log.error(e);
} }
finally { finally {
try { try { if (ctx != null) { ctx.close(); } }
ctx.close(); catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
}
} }
} }
// Otherwise, we're using the mixed LDAP mode. // Otherwise, we're using the mixed LDAP mode.
else { else {
Connection con = null; Connection con = null;
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
try { try {
con = DbConnectionManager.getConnection(); con = DbConnectionManager.getConnection();
pstmt = con.prepareStatement(USER_COUNT); pstmt = con.prepareStatement(USER_COUNT);
...@@ -192,37 +138,21 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -192,37 +138,21 @@ public class LdapUserIDProvider implements UserIDProvider {
if (rs.next()) { if (rs.next()) {
count = rs.getInt(1); count = rs.getInt(1);
} }
rs.close();
} }
catch (SQLException e) { catch (SQLException e) {
Log.error(e); Log.error(e);
} }
finally { finally {
try { try { if (pstmt != null) { pstmt.close(); } }
if (pstmt != null) { catch (Exception e) { Log.error(e); }
pstmt.close(); try { if (con != null) { con.close(); } }
} catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) {
con.close();
}
}
catch (Exception e) {
Log.error(e);
}
} }
} }
return count; return count;
} }
/**
* <p>Obtain a list all user IDs on the system.</p>
*
* @return LongList of user ID's
*/
public LongList getUserIDs() { public LongList getUserIDs() {
LongList users = new LongList(500); LongList users = new LongList(500);
...@@ -239,31 +169,20 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -239,31 +169,20 @@ public class LdapUserIDProvider implements UserIDProvider {
while (rs.next()) { while (rs.next()) {
users.add(rs.getLong(1)); users.add(rs.getLong(1));
} }
rs.close();
} }
catch (SQLException e) { catch (SQLException e) {
Log.error(e); Log.error(e);
} }
finally { finally {
try { try { if (pstmt != null) { pstmt.close(); } }
if (pstmt != null) { catch (Exception e) { Log.error(e); }
pstmt.close(); try { if (con != null) { con.close(); } }
} catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) {
con.close();
}
}
catch (Exception e) {
Log.error(e);
}
} }
return users; return users;
} }
// else, in LDAP-only mode // Otherwise, in LDAP-only mode.
DirContext ctx = null; DirContext ctx = null;
try { try {
ctx = manager.getContext(); ctx = manager.getContext();
...@@ -283,22 +202,12 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -283,22 +202,12 @@ public class LdapUserIDProvider implements UserIDProvider {
Log.error(e); Log.error(e);
} }
finally { finally {
try { try { if (ctx != null) { ctx.close(); } }
ctx.close(); catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
}
} }
return users; return users;
} }
/**
* Get paginated sublist of userID's
*
* @param startIndex index to begin sublist with.
* @param numResults maximum number of results to return.
* @return sublist of userID's.
*/
public LongList getUserIDs(int startIndex, int numResults) { public LongList getUserIDs(int startIndex, int numResults) {
LongList users = new LongList(); LongList users = new LongList();
if (manager.getMode() == LdapManager.LDAP_DB_MODE) { // if in mixed mode, get id's from DB. if (manager.getMode() == LdapManager.LDAP_DB_MODE) { // if in mixed mode, get id's from DB.
...@@ -328,22 +237,10 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -328,22 +237,10 @@ public class LdapUserIDProvider implements UserIDProvider {
Log.error(e); Log.error(e);
} }
finally { finally {
try { try { if (pstmt != null) { pstmt.close(); } }
if (pstmt != null) { catch (Exception e) { Log.error(e); }
pstmt.close(); try { if (con != null) { con.close(); } }
} catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) {
con.close();
}
}
catch (Exception e) {
Log.error(e);
}
} }
return users; return users;
} }
...@@ -375,21 +272,19 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -375,21 +272,19 @@ public class LdapUserIDProvider implements UserIDProvider {
Log.error(e); Log.error(e);
} }
finally { finally {
try { try { if (ctx != null) { ctx.close(); } }
ctx.close(); catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
}
} }
return users; return users;
} }
/** /**
* This is used when operating in mixed mode -- generate a new user ID for a user stored in our database. * This is used when operating in mixed mode -- generate a new user ID for a
* user stored in our database.
* *
* @param username * @param username the username.
* @return id corresponding to that username * @return id corresponding to that username
* @throws UserNotFoundException * @throws UserNotFoundException if an error occured generating the user ID.
*/ */
private long generateNewUserIDLocally(String username) throws UserNotFoundException { private long generateNewUserIDLocally(String username) throws UserNotFoundException {
long id = -1; long id = -1;
...@@ -410,22 +305,10 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -410,22 +305,10 @@ public class LdapUserIDProvider implements UserIDProvider {
throw new UserNotFoundException(e); throw new UserNotFoundException(e);
} }
finally { finally {
try { try { if (pstmt != null) { pstmt.close(); } }
if (pstmt != null) { catch (Exception e) { Log.error(e); }
pstmt.close(); try { if (con != null) { con.close(); } }
} catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) {
con.close();
}
}
catch (Exception e) {
Log.error(e);
}
} }
return id; return id;
} }
...@@ -451,27 +334,16 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -451,27 +334,16 @@ public class LdapUserIDProvider implements UserIDProvider {
if (rs.next()) { if (rs.next()) {
id = rs.getLong(1); id = rs.getLong(1);
} }
rs.close();
} }
catch (Exception e) { catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e); Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
} }
finally { finally {
try { try { if (pstmt != null) { pstmt.close(); } }
if (pstmt != null) { catch (Exception e) { Log.error(e); }
pstmt.close(); try { if (con != null) { con.close(); } }
} catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) {
con.close();
}
}
catch (Exception e) {
Log.error(e);
}
} }
if (id == -1) { if (id == -1) {
throw new UserNotFoundException(); throw new UserNotFoundException();
...@@ -482,9 +354,10 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -482,9 +354,10 @@ public class LdapUserIDProvider implements UserIDProvider {
/** /**
* This method is used when operating in pure LDAP mode. Get user ID from the LDAP store. * This method is used when operating in pure LDAP mode. Get user ID from the LDAP store.
* *
* @param username * @param username the username.
* @return user id corresponding to that username. * @return user id corresponding to that username.
* @throws UserNotFoundException * @throws UserNotFoundException if ther was an error loading the username
* from LDAP.
*/ */
private long getUserIDFromLdap(String username) throws UserNotFoundException { private long getUserIDFromLdap(String username) throws UserNotFoundException {
DirContext ctx = null; DirContext ctx = null;
...@@ -500,21 +373,17 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -500,21 +373,17 @@ public class LdapUserIDProvider implements UserIDProvider {
throw new UserNotFoundException(e); throw new UserNotFoundException(e);
} }
finally { finally {
try { try { if (ctx != null) { ctx.close(); } }
ctx.close(); catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
}
} }
} }
/** /**
* This method is used in mixed mode. Get the username that corresponds to a given ID. * This method is used in mixed mode. Get the username that corresponds to a given ID.
* *
* @param id * @param id the user ID.
* @return username for that user id. * @return username for that user id.
* @throws UserNotFoundException * @throws UserNotFoundException if there was an error loading the username.
*/ */
private String getUsernameFromDb(long id) throws UserNotFoundException { private String getUsernameFromDb(long id) throws UserNotFoundException {
String name = null; String name = null;
...@@ -530,27 +399,16 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -530,27 +399,16 @@ public class LdapUserIDProvider implements UserIDProvider {
if (rs.next()) { if (rs.next()) {
name = rs.getString(1); name = rs.getString(1);
} }
rs.close();
} }
catch (Exception e) { catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e); Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
} }
finally { finally {
try { try { if (pstmt != null) { pstmt.close(); } }
if (pstmt != null) { catch (Exception e) { Log.error(e); }
pstmt.close(); try { if (con != null) { con.close(); } }
} catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
Log.error(e);
}
try {
if (con != null) {
con.close();
}
}
catch (Exception e) {
Log.error(e);
}
} }
if (name == null) { if (name == null) {
throw new UserNotFoundException(); throw new UserNotFoundException();
...@@ -559,10 +417,11 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -559,10 +417,11 @@ public class LdapUserIDProvider implements UserIDProvider {
} }
/** /**
* This method is used when operating in pure LDAP mode. Get the username that corresponds to a given ID. * This method is used when operating in pure LDAP mode. Get the username
* that corresponds to a given ID.
* *
* @param id * @param id the userID.
* @return * @return ther username.
* @throws UserNotFoundException * @throws UserNotFoundException
*/ */
private String getUsernameFromLdap(long id) throws UserNotFoundException { private String getUsernameFromLdap(long id) throws UserNotFoundException {
...@@ -581,7 +440,6 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -581,7 +440,6 @@ public class LdapUserIDProvider implements UserIDProvider {
} }
String userDN = ((SearchResult)answer.next()).getName(); String userDN = ((SearchResult)answer.next()).getName();
return getUsernameFromUserDN(userDN); return getUsernameFromUserDN(userDN);
} }
catch (NamingException e) { catch (NamingException e) {
Log.error(e); Log.error(e);
...@@ -592,13 +450,9 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -592,13 +450,9 @@ public class LdapUserIDProvider implements UserIDProvider {
throw new UserNotFoundException(e); throw new UserNotFoundException(e);
} }
finally { finally {
try { try { if (ctx != null) { ctx.close(); } }
ctx.close(); catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
}
} }
} }
/** /**
...@@ -618,12 +472,8 @@ public class LdapUserIDProvider implements UserIDProvider { ...@@ -618,12 +472,8 @@ public class LdapUserIDProvider implements UserIDProvider {
return (String)attrs.get(manager.getUsernameField()).get(); return (String)attrs.get(manager.getUsernameField()).get();
} }
finally { finally {
try { try { if (ctx != null) { ctx.close(); } }
ctx.close(); catch (Exception e) { Log.error(e); }
}
catch (Exception e) {
}
} }
} }
} }
\ No newline at end of file
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