LdapManager.java 29 KB
Newer Older
Matt Tucker's avatar
Matt Tucker committed
1 2 3 4 5
/**
 * $RCSfile$
 * $Revision$
 * $Date$
 *
Matt Tucker's avatar
Matt Tucker committed
6
 * Copyright (C) 2004 Jive Software. All rights reserved.
Matt Tucker's avatar
Matt Tucker committed
7
 *
Matt Tucker's avatar
Matt Tucker committed
8 9
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
Matt Tucker's avatar
Matt Tucker committed
10 11 12 13
 */

package org.jivesoftware.messenger.ldap;

14
import org.jivesoftware.messenger.user.UserNotFoundException;
15
import org.jivesoftware.util.JiveGlobals;
Matt Tucker's avatar
Matt Tucker committed
16 17
import org.jivesoftware.util.Log;

18 19 20 21 22 23 24
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
25
import javax.naming.ldap.InitialLdapContext;
26 27 28
import javax.naming.ldap.LdapContext;
import java.net.URLEncoder;
import java.util.Hashtable;
Matt Tucker's avatar
Matt Tucker committed
29 30 31

/**
 * Centralized administration of LDAP connections. The getInstance() method
Matt Tucker's avatar
Matt Tucker committed
32
 * should be used to get an instace. The following configure this manager:<ul>
33 34 35
 *      <li>ldap.host</li>
 *      <li>ldap.port</li>
 *      <li>ldap.baseDN</li>
36
 *      <li>ldap.alternateBaseDN</li>
37 38 39 40 41
 *      <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>
42 43 44
 *      <li>ldap.searchFilter -- the filter used to load the list of users. The
 *              default value is in the form "([usernameField]={0})" where [usernameField]
 *              is the value of ldap.usernameField.
45 46 47 48 49
 *      <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>
Matt Tucker's avatar
Matt Tucker committed
50
 * </ul>
Matt Tucker's avatar
Matt Tucker committed
51 52 53 54 55 56 57 58 59 60 61
 *
 * @author Matt Tucker
 */
public class LdapManager {

    private String host;
    private int port = 389;
    private String usernameField = "uid";
    private String nameField = "cn";
    private String emailField = "mail";
    private String baseDN = "";
Matt Tucker's avatar
Matt Tucker committed
62
    private String alternateBaseDN = null;
63
    private String adminDN = null;
Matt Tucker's avatar
Matt Tucker committed
64
    private String adminPassword;
Matt Tucker's avatar
Matt Tucker committed
65
    private boolean ldapDebugEnabled = false;
Matt Tucker's avatar
Matt Tucker committed
66
    private boolean sslEnabled = false;
Matt Tucker's avatar
Matt Tucker committed
67
    private String initialContextFactory;
68
    private boolean followReferrals = false;
Matt Tucker's avatar
Matt Tucker committed
69
    private boolean connectionPoolEnabled = true;
70
    private String searchFilter = null;
Matt Tucker's avatar
Matt Tucker committed
71 72 73 74 75 76 77 78 79 80 81 82 83

    private static LdapManager instance = new LdapManager();

    /**
     * Provides singleton access to an instance of the LdapManager class.
     *
     * @return an LdapManager instance.
     */
    public static LdapManager getInstance() {
        return instance;
    }

    /**
Matt Tucker's avatar
Matt Tucker committed
84
     * Constructs a new LdapManager instance. This class is a singleton so the
Matt Tucker's avatar
Matt Tucker committed
85 86 87
     * constructor is private.
     */
    private LdapManager() {
88 89
        this.host = JiveGlobals.getXMLProperty("ldap.host");
        String portStr = JiveGlobals.getXMLProperty("ldap.port");
Matt Tucker's avatar
Matt Tucker committed
90 91 92 93
        if (portStr != null) {
            try {
                this.port = Integer.parseInt(portStr);
            }
Matt Tucker's avatar
Matt Tucker committed
94
            catch (NumberFormatException nfe) { }
Matt Tucker's avatar
Matt Tucker committed
95
        }
96 97
        if (JiveGlobals.getXMLProperty("ldap.usernameField") != null) {
            this.usernameField = JiveGlobals.getXMLProperty("ldap.usernameField");
Matt Tucker's avatar
Matt Tucker committed
98
        }
99 100
        if (JiveGlobals.getXMLProperty("ldap.baseDN") != null) {
            this.baseDN = JiveGlobals.getXMLProperty("ldap.baseDN");
Matt Tucker's avatar
Matt Tucker committed
101
        }
Matt Tucker's avatar
Matt Tucker committed
102 103 104
        if (JiveGlobals.getXMLProperty("ldap.alternateBaseDN") != null) {
            this.alternateBaseDN = JiveGlobals.getXMLProperty("ldap.alternateBaseDN");
        }
105 106
        if (JiveGlobals.getXMLProperty("ldap.nameField") != null) {
            this.nameField = JiveGlobals.getXMLProperty("ldap.nameField");
Matt Tucker's avatar
Matt Tucker committed
107
        }
108 109
        if (JiveGlobals.getXMLProperty("ldap.emailField") != null) {
            this.emailField = JiveGlobals.getXMLProperty("ldap.emailField");
Matt Tucker's avatar
Matt Tucker committed
110
        }
Matt Tucker's avatar
Matt Tucker committed
111 112 113 114
        if (JiveGlobals.getXMLProperty("ldap.connectionPoolEnabled") != null) {
            this.connectionPoolEnabled = Boolean.valueOf(
                    JiveGlobals.getXMLProperty("ldap.connectionPoolEnabled")).booleanValue();
        }
115 116 117 118
        if (JiveGlobals.getXMLProperty("ldap.searchFilter") != null) {
            this.searchFilter = JiveGlobals.getXMLProperty("ldap.searchFilter");
        }
        else {
119
            StringBuilder filter = new StringBuilder();
120 121 122 123
            filter.append("(").append(usernameField).append("={0})");
            this.searchFilter = filter.toString();
        }

124
        this.adminDN = JiveGlobals.getXMLProperty("ldap.adminDN");
125 126 127
        if (adminDN != null && adminDN.trim().equals("")) {
            adminDN = null;
        }
128
        this.adminPassword = JiveGlobals.getXMLProperty("ldap.adminPassword");
129 130 131 132 133 134
        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();
Matt Tucker's avatar
Matt Tucker committed
135 136 137 138 139 140 141 142 143
        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";
Matt Tucker's avatar
Matt Tucker committed
144 145
            }
        }
Matt Tucker's avatar
Matt Tucker committed
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
        // 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);
162
            Log.debug("\t searchFilter: " + searchFilter);
Matt Tucker's avatar
Matt Tucker committed
163 164 165 166
            Log.debug("\t ldapDebugEnabled: " + ldapDebugEnabled);
            Log.debug("\t sslEnabled: " + sslEnabled);
            Log.debug("\t initialContextFactory: " + initialContextFactory);
            Log.debug("\t connectionPoolEnabled: " + connectionPoolEnabled);
167
            Log.debug("\t autoFollowReferrals: " + followReferrals);
Matt Tucker's avatar
Matt Tucker committed
168
        }
Matt Tucker's avatar
Matt Tucker committed
169 170 171 172
    }

    /**
     * Returns a DirContext for the LDAP server that can be used to perform
Matt Tucker's avatar
Matt Tucker committed
173 174
     * lookups and searches using the default base DN. The context uses the
     * admin login that is defined by <tt>adminDN</tt> and <tt>adminPassword</tt>.
Matt Tucker's avatar
Matt Tucker committed
175 176 177 178
     *
     * @return a connection to the LDAP server.
     * @throws NamingException if there is an error making the LDAP connection.
     */
179
    public LdapContext getContext() throws NamingException {
Matt Tucker's avatar
Matt Tucker committed
180 181 182 183 184 185 186 187 188 189 190 191
        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.
     */
192
    public LdapContext getContext(String baseDN) throws NamingException {
Matt Tucker's avatar
Matt Tucker committed
193 194 195 196 197 198
        boolean debug = Log.isDebugEnabled();
        if (debug) {
            Log.debug("Creating a DirContext in LdapManager.getContext()...");
        }

         // Set up the environment for creating the initial context
Matt Tucker's avatar
Matt Tucker committed
199
        Hashtable env = new Hashtable();
Matt Tucker's avatar
Matt Tucker committed
200 201
        env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
        env.put(Context.PROVIDER_URL, getProviderURL(baseDN));
Matt Tucker's avatar
Matt Tucker committed
202
        if (sslEnabled) {
Matt Tucker's avatar
Matt Tucker committed
203
            env.put("java.naming.ldap.factory.socket",
Matt Tucker's avatar
Matt Tucker committed
204
                    "org.jivesoftware.util.SimpleSSLSocketFactory");
Matt Tucker's avatar
Matt Tucker committed
205 206 207 208 209
            env.put(Context.SECURITY_PROTOCOL, "ssl");
        }

        // Use simple authentication to connect as the admin.
        if (adminDN != null) {
210
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
Matt Tucker's avatar
Matt Tucker committed
211
            env.put(Context.SECURITY_PRINCIPAL, adminDN);
212 213 214
            if (adminPassword != null) {
                env.put(Context.SECURITY_CREDENTIALS, adminPassword);
            }
Matt Tucker's avatar
Matt Tucker committed
215
        }
216
        // No login information so attempt to use anonymous login.
Matt Tucker's avatar
Matt Tucker committed
217
        else {
218
            env.put(Context.SECURITY_AUTHENTICATION, "none");
Matt Tucker's avatar
Matt Tucker committed
219
        }
220

Matt Tucker's avatar
Matt Tucker committed
221
        if (ldapDebugEnabled) {
Matt Tucker's avatar
Matt Tucker committed
222 223
            env.put("com.sun.jndi.ldap.trace.ber", System.err);
        }
Matt Tucker's avatar
Matt Tucker committed
224 225 226
        if (connectionPoolEnabled) {
            env.put("com.sun.jndi.ldap.connect.pool", "true");
        }
227 228 229
        if (followReferrals) {
            env.put(Context.REFERRAL, "follow");
        }
Matt Tucker's avatar
Matt Tucker committed
230

Matt Tucker's avatar
Matt Tucker committed
231 232 233
        if (debug) {
            Log.debug("Created hashtable with context values, attempting to create context...");
        }
Matt Tucker's avatar
Matt Tucker committed
234
        // Create new initial context
235
        LdapContext context = new InitialLdapContext(env, null);
Matt Tucker's avatar
Matt Tucker committed
236 237 238 239
        if (debug) {
            Log.debug("... context created successfully, returning.");
        }
        return context;
Matt Tucker's avatar
Matt Tucker committed
240 241 242 243 244 245
    }

    /**
     * Returns true if the user is able to successfully authenticate against
     * the LDAP server. The "simple" authentication protocol is used.
     *
Matt Tucker's avatar
Matt Tucker committed
246
     * @param userDN the user's dn to authenticate (relative to <tt>baseDN</tt>).
Matt Tucker's avatar
Matt Tucker committed
247 248 249 250
     * @param password the user's password.
     * @return true if the user successfully authenticates.
     */
    public boolean checkAuthentication(String userDN, String password) {
Matt Tucker's avatar
Matt Tucker committed
251 252 253 254 255
        boolean debug = Log.isDebugEnabled();
        if (debug) {
            Log.debug("In LdapManager.checkAuthentication(userDN, password), userDN is: " + userDN + "...");
        }

Matt Tucker's avatar
Matt Tucker committed
256 257 258 259
        DirContext ctx = null;
        try {
            // See if the user authenticates.
            Hashtable env = new Hashtable();
Matt Tucker's avatar
Matt Tucker committed
260 261
            env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
            env.put(Context.PROVIDER_URL, getProviderURL(baseDN));
Matt Tucker's avatar
Matt Tucker committed
262
            if (sslEnabled) {
Matt Tucker's avatar
Matt Tucker committed
263
                env.put("java.naming.ldap.factory.socket", "org.jivesoftware.util.SimpleSSLSocketFactory");
Matt Tucker's avatar
Matt Tucker committed
264 265 266 267 268
                env.put(Context.SECURITY_PROTOCOL, "ssl");
            }
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL, userDN + "," + baseDN);
            env.put(Context.SECURITY_CREDENTIALS, password);
Matt Tucker's avatar
Matt Tucker committed
269
            if (ldapDebugEnabled) {
Matt Tucker's avatar
Matt Tucker committed
270 271
                env.put("com.sun.jndi.ldap.trace.ber", System.err);
            }
Matt Tucker's avatar
Matt Tucker committed
272 273 274
            if (debug) {
                Log.debug("Created context values, attempting to create context...");
            }
Matt Tucker's avatar
Matt Tucker committed
275
            ctx = new InitialDirContext(env);
Matt Tucker's avatar
Matt Tucker committed
276 277 278
            if (debug) {
                Log.debug("... context created successfully, returning.");
            }
Matt Tucker's avatar
Matt Tucker committed
279 280
        }
        catch (NamingException ne) {
Matt Tucker's avatar
Matt Tucker committed
281 282 283 284 285 286 287 288 289 290 291
            // If an alt baseDN is defined, attempt a lookup there.
            if (alternateBaseDN != null) {
                try { ctx.close(); }
                catch (Exception ignored) { }
                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) {
Matt Tucker's avatar
Matt Tucker committed
292
                        env.put("java.naming.ldap.factory.socket", "org.jivesoftware.util.SimpleSSLSocketFactory");
Matt Tucker's avatar
Matt Tucker committed
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
                        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;
                }
Matt Tucker's avatar
Matt Tucker committed
312
            }
Matt Tucker's avatar
Matt Tucker committed
313 314 315 316 317
            else {
                if (debug) {
                    Log.debug("Caught a naming exception when creating InitialContext", ne);
                }
                return false;
Matt Tucker's avatar
Matt Tucker committed
318 319
            }
        }
Matt Tucker's avatar
Matt Tucker committed
320 321 322 323
        finally {
            try { ctx.close(); }
            catch (Exception ignored) { }
        }
Matt Tucker's avatar
Matt Tucker committed
324 325 326 327 328 329 330
        return true;
    }

    /**
     * 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
     * the <tt>usernameField</tt> property.<p>
Matt Tucker's avatar
Matt Tucker committed
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
     *
     * 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>
     *
Matt Tucker's avatar
Matt Tucker committed
370 371 372 373 374 375 376 377 378 379 380
     * 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
     * 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>
Matt Tucker's avatar
Matt Tucker committed
381
     *
Matt Tucker's avatar
Matt Tucker committed
382 383 384
     * The dn that's returned is relative to the <tt>baseDN</tt>.
     *
     * @param username the username to lookup the dn for.
Matt Tucker's avatar
Matt Tucker committed
385
     * @param baseDN the base DN to use for this search.
Matt Tucker's avatar
Matt Tucker committed
386 387
     * @return the dn associated with <tt>username</tt>.
     * @throws Exception if the search for the dn fails.
Matt Tucker's avatar
Matt Tucker committed
388
     * @see #findUserDN(String)  to search using the default baseDN and alternateBaseDN.
Matt Tucker's avatar
Matt Tucker committed
389
     */
Matt Tucker's avatar
Matt Tucker committed
390 391 392 393 394 395
    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 + "...");
        }
Matt Tucker's avatar
Matt Tucker committed
396 397
        DirContext ctx = null;
        try {
Matt Tucker's avatar
Matt Tucker committed
398 399 400 401
            ctx = getContext(baseDN);
            if (debug) {
                Log.debug("Starting LDAP search...");
            }
Matt Tucker's avatar
Matt Tucker committed
402 403 404
            // Search for the dn based on the username.
            SearchControls constraints = new SearchControls();
            constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
Matt Tucker's avatar
Matt Tucker committed
405
            constraints.setReturningAttributes(new String[] { usernameField });
Matt Tucker's avatar
Matt Tucker committed
406

407
            NamingEnumeration answer = ctx.search("", searchFilter, new String[] {username}, constraints);
Matt Tucker's avatar
Matt Tucker committed
408 409 410 411 412

            if (debug) {
                Log.debug("... search finished");
            }

Matt Tucker's avatar
Matt Tucker committed
413
            if (answer == null || !answer.hasMoreElements()) {
Matt Tucker's avatar
Matt Tucker committed
414 415 416
                if (debug) {
                    Log.debug("User DN based on username '" + username + "' not found.");
                }
417
                throw new UserNotFoundException("Username " + username + " not found");
Matt Tucker's avatar
Matt Tucker committed
418 419 420
            }
            String userDN = ((SearchResult)answer.next()).getName();
            // Make sure there are no more search results. If there are, then
421
            // the username isn't unique on the LDAP server (a perfectly possible
Matt Tucker's avatar
Matt Tucker committed
422 423 424 425
            // 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()) {
Matt Tucker's avatar
Matt Tucker committed
426 427 428 429
                if (debug) {
                    Log.debug("Search for userDN based on username '" + username + "' found multiple " +
                            "responses, throwing exception.");
                }
430
                throw new UserNotFoundException("LDAP username lookup for " + username +
Matt Tucker's avatar
Matt Tucker committed
431 432 433 434
                        " matched multiple entries.");
            }
            return userDN;
        }
Matt Tucker's avatar
Matt Tucker committed
435 436 437
        catch (Exception e) {
            if (debug) {
                Log.debug("Exception thrown when searching for userDN based on username '" + username + "'", e);
Matt Tucker's avatar
Matt Tucker committed
438
            }
Matt Tucker's avatar
Matt Tucker committed
439 440 441 442 443
            throw e;
        }
        finally {
            try { ctx.close(); }
            catch (Exception ignored) { }
Matt Tucker's avatar
Matt Tucker committed
444 445 446
        }
    }

Matt Tucker's avatar
Matt Tucker committed
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
    /**
     * 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;
    }

Matt Tucker's avatar
Matt Tucker committed
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
    /**
     * Returns the LDAP server host; e.g. <tt>localhost</tt> or
     * <tt>machine.example.com</tt>, etc. This value is stored as the Jive
     * Property <tt>ldap.host</tt>.
     *
     * @return the LDAP server host name.
     */
    public String getHost() {
        return host;
    }

    /**
     * Sets the LDAP server host; e.g., <tt>localhost</tt> or
     * <tt>machine.example.com</tt>, etc. This value is store as the Jive
     * Property <tt>ldap.host</tt>
     *
     * @param host the LDAP server host name.
     */
    public void setHost(String host) {
        this.host = host;
490
        JiveGlobals.setXMLProperty("ldap.host", host);
Matt Tucker's avatar
Matt Tucker committed
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
    }

    /**
     * Returns the LDAP server port number. The default is 389. This value is
     * stored as the Jive Property <tt>ldap.port</tt>.
     *
     * @return the LDAP server port number.
     */
    public int getPort() {
        return port;
    }

    /**
     * Sets the LDAP server port number. The default is 389. This value is
     * stored as the Jive property <tt>ldap.port</tt>.
     *
     * @param port the LDAP server port number.
     */
    public void setPort(int port) {
        this.port = port;
Matt Tucker's avatar
Matt Tucker committed
511
        JiveGlobals.setXMLProperty("ldap.port", ""+port);
Matt Tucker's avatar
Matt Tucker committed
512 513 514 515 516 517 518 519 520 521
    }

    /**
     * Returns true if LDAP connection debugging is turned on. When on, trace
     * information about BER buffers sent and received by the LDAP provider is
     * written to System.out. Debugging is turned off by default.
     *
     * @return true if LDAP debugging is turned on.
     */
    public boolean isDebugEnabled() {
Matt Tucker's avatar
Matt Tucker committed
522
        return ldapDebugEnabled;
Matt Tucker's avatar
Matt Tucker committed
523 524 525 526 527 528 529 530 531 532
    }

    /**
     * Sets whether LDAP connection debugging is turned on. When on, trace
     * information about BER buffers sent and received by the LDAP provider is
     * written to System.out. Debugging is turned off by default.
     *
     * @param debugEnabled true if debugging should be turned on.
     */
    public void setDebugEnabled(boolean debugEnabled) {
Matt Tucker's avatar
Matt Tucker committed
533 534
        this.ldapDebugEnabled = debugEnabled;
        JiveGlobals.setXMLProperty("ldap.ldapDebugEnabled", ""+debugEnabled);
Matt Tucker's avatar
Matt Tucker committed
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
    }

    /**
     * Returns true if LDAP connection is via SSL or not. SSL is turned off by default.
     *
     * @return true if SSL connections are enabled or not.
     */
    public boolean isSslEnabled() {
        return sslEnabled;
    }

    /**
     * Sets whether the connection to the LDAP server should be made via ssl or not.
     *
     * @param sslEnabled true if ssl should be enabled, false otherwise.
     */
    public void setSslEnabled(boolean sslEnabled) {
        this.sslEnabled = sslEnabled;
Matt Tucker's avatar
Matt Tucker committed
553
        JiveGlobals.setXMLProperty("ldap.sslEnabled", ""+sslEnabled);
Matt Tucker's avatar
Matt Tucker committed
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
    }

    /**
     * Returns the LDAP field name that the username lookup will be performed
     * on. By default this is "uid".
     *
     * @return the LDAP field that the username lookup will be performed on.
     */
    public String getUsernameField() {
        return usernameField;
    }

    /**
     * Sets the LDAP field name that the username lookup will be performed on.
     * By default this is "uid".
     *
     * @param usernameField the LDAP field that the username lookup will be
Matt Tucker's avatar
Matt Tucker committed
571
     *      performed on.
Matt Tucker's avatar
Matt Tucker committed
572 573 574 575
     */
    public void setUsernameField(String usernameField) {
        this.usernameField = usernameField;
        if (usernameField == null) {
576
            JiveGlobals.deleteXMLProperty("ldap.usernameField");
Matt Tucker's avatar
Matt Tucker committed
577 578
        }
        else {
579
            JiveGlobals.setXMLProperty("ldap.usernameField", usernameField);
Matt Tucker's avatar
Matt Tucker committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
        }
    }

    /**
     * Returns the LDAP field name that the user's name is stored in. By default
     * this is "cn". Another common value is "displayName".
     *
     * @return the LDAP field that that correspond's to the user's name.
     */
    public String getNameField() {
        return nameField;
    }

    /**
     * Sets the LDAP field name that the user's name is stored in. By default
     * this is "cn". Another common value is "displayName".
     *
     * @param nameField the LDAP field that that correspond's to the user's name.
     */
    public void setNameField(String nameField) {
        this.nameField = nameField;
        if (nameField == null) {
602
            JiveGlobals.deleteXMLProperty("ldap.nameField");
Matt Tucker's avatar
Matt Tucker committed
603 604
        }
        else {
605
            JiveGlobals.setXMLProperty("ldap.nameField", nameField);
Matt Tucker's avatar
Matt Tucker committed
606 607 608 609 610 611 612 613
        }
    }

    /**
     * Returns the LDAP field name that the user's email address is stored in.
     * By default this is "mail".
     *
     * @return the LDAP field that that correspond's to the user's email
Matt Tucker's avatar
Matt Tucker committed
614
     *      address.
Matt Tucker's avatar
Matt Tucker committed
615 616 617 618 619 620 621 622 623 624
     */
    public String getEmailField() {
        return emailField;
    }

    /**
     * Sets the LDAP field name that the user's email address is stored in.
     * By default this is "mail".
     *
     * @param emailField the LDAP field that that correspond's to the user's
Matt Tucker's avatar
Matt Tucker committed
625
     *      email address.
Matt Tucker's avatar
Matt Tucker committed
626 627 628 629
     */
    public void setEmailField(String emailField) {
        this.emailField = emailField;
        if (emailField == null) {
630
            JiveGlobals.deleteXMLProperty("ldap.emailField");
Matt Tucker's avatar
Matt Tucker committed
631 632
        }
        else {
633
            JiveGlobals.setXMLProperty("ldap.emailField", emailField);
Matt Tucker's avatar
Matt Tucker committed
634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654
        }
    }

    /**
     * Returns the starting DN that searches for users will performed with.
     * Searches will performed on the entire sub-tree under the base DN.
     *
     * @return the starting DN used for performing searches.
     */
    public String getBaseDN() {
        return baseDN;
    }

    /**
     * Sets the starting DN that searches for users will performed with.
     * Searches will performed on the entire sub-tree under the base DN.
     *
     * @param baseDN the starting DN used for performing searches.
     */
    public void setBaseDN(String baseDN) {
        this.baseDN = baseDN;
655
        JiveGlobals.setXMLProperty("ldap.baseDN", baseDN);
Matt Tucker's avatar
Matt Tucker committed
656 657
    }

Matt Tucker's avatar
Matt Tucker committed
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
    /**
     * 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);
        }
    }

Matt Tucker's avatar
Matt Tucker committed
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
    /**
     * Returns the starting admin DN that searches for admins will performed with.
     * Searches will performed on the entire sub-tree under the admin DN.
     *
     * @return the starting DN used for performing searches.
     */
    public String getAdminDN() {
        return adminDN;
    }

    /**
     * Sets the starting admin DN that searches for admins will performed with.
     * Searches will performed on the entire sub-tree under the admins DN.
     *
     * @param adminDN the starting DN used for performing admin searches.
     */
    public void setAdminDN(String adminDN) {
        this.adminDN = adminDN;
705
        JiveGlobals.setXMLProperty("ldap.adminDN", adminDN);
Matt Tucker's avatar
Matt Tucker committed
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
    }

    /**
     * Returns the starting admin DN that searches for admins will performed with.
     * Searches will performed on the entire sub-tree under the admin DN.
     *
     * @return the starting DN used for performing searches.
     */
    public String getAdminPassword() {
        return adminPassword;
    }

    /**
     * Sets the admin password for the LDAP server we're connecting to.
     *
     * @param adminPassword the admin password for the LDAP server we're
Matt Tucker's avatar
Matt Tucker committed
722
     * connecting to.
Matt Tucker's avatar
Matt Tucker committed
723 724 725
     */
    public void setAdminPassword(String adminPassword) {
        this.adminPassword = adminPassword;
726
        JiveGlobals.setXMLProperty("ldap.adminPassword", adminPassword);
Matt Tucker's avatar
Matt Tucker committed
727
    }
728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746

    /**
     * Returns the filter used for searching the directory for users.
     *
     * @return the search filter.
     */
    public String getSearchFilter() {
    	return searchFilter;
    }

    /**
     * Sets the filter used for searching the directory for users. The filter should
     * contain a single token "{0}" that will be dynamically replaced with the
     * user's unique ID.
     *
     * @param searchFilter the search filter.
     */
    public void setSearchFilter(String searchFilter) {
    	if (searchFilter == null || "".equals(searchFilter)) {
747
            StringBuilder filter = new StringBuilder();
748 749 750 751 752 753 754 755 756
            filter.append("(").append(usernameField).append("={0})");
            this.searchFilter = filter.toString();
            JiveGlobals.deleteXMLProperty("ldap.searchFilter");
        }
        else {
            this.searchFilter = searchFilter;
    		JiveGlobals.setXMLProperty("ldap.searchFilter", searchFilter);
    	}
    }
Matt Tucker's avatar
Matt Tucker committed
757
}