# Wildfire LDAP Guide

## Introduction

This document details how to configure your Wildfire installation to use an external LDAP store when authenticating users and loading user profile information.

## Background

LDAP (Lightweight Directory Access Protocol) has emerged as a dominant standard for user authentication and for storage of user profile data. It serves as a powerful tool for large organizations (or those organizations integrating many applications) to simplify user management issues.

By default, Wildfire stores all user data in a database and performs authentication using database lookups. The LDAP module replaces that functionality and allows Wildfire to:

- Use a LDAP server to authenticate a user's identity.
- Load user profile information from a LDAP directory.
- Load group information from an LDAP directory.

Note: Wildfire treats the LDAP directory as read-only.

This document will guide you through configuring LDAP support in Wildfire. These instructions assume that you're a competent LDAP user, and that you're familiar with Wildfire setup issues.

## Configuration

In order to configure your server to use LDAP:

1. Stop Wildfire.
2. Edit `conf/wildfire.xml` in your Wildfire installation folder as described below.
3. Restart Wildfire.

### Editing the Config File

Open the configuration file `conf/wildfire.xml` from your Wildfire installation in your favorite editor and add or change the following settings. Properties flagged with ( *) must be set. Properties flagged with (**) must be set in order to enable LDAP group support, all other properties are optional:

- provider.user.className * -- set the value to "org.jivesoftware.wildfire.ldap.LdapUserProvider".
- provider.auth.className * -- set the value to "org.jivesoftware.wildfire.ldap.LdapAuthProvider".
- provider.group.className ** -- set the value to "org.jivesoftware.wildfire.ldap.LdapGroupProvider".
- ldap.host * -- LDAP server host; e.g. localhost or machine.example.com, etc.
- ldap.port -- LDAP server port number. If this property is not set, the default value is 389.
- 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.
- ldap.alternateBaseDN -- a second DN in the directory can optionally be set. If set, the alternate base DN will be used for authentication and loading single users, but will not be used to display a list of users (due to technical limitations).
- 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 Wildfire treats the directory as read-only. If this property is not set, an anonymous login to the server will be attempted.
- ldap.adminPassword -- the password for the directory administrator.
- ldap.usernameField -- the field name that the username lookups will be performed on. If this property is not set, the default value is `uid`
- ldap.nameField -- the field name that holds the user's name. If this property is not set, the default value is `cn`
- ldap.emailField -- the field name that holds the user's email address. If this property is not set, the default value is `mail`.
- ldap.searchFilter -- the search filter that should be used when loading users. If this property is not set, the default search will be for users that have the attribute specified by ldap.usernameField.
- 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
- 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.
- 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.
- 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.
- ldap.connectionPoolEnabled -- a value of "false" disables LDAP connection pooling. If this property is not set, the default value is "true".
- ldap.groupNameField -- the field name that the groupname lookups will be performed on. If this property is not set, the default value is `cn`
- ldap.groupMemberField -- the field name that holds the members in a group. If this property is not set, the default value is `member`
- ldap.groupDescriptionField -- the field name that holds the description a group. If this property is not set, the default value is `description`
- ldap.posixMode -- a value of "true" means that users are stored within the group by their user name alone. A value of "false" means that users are stored by their entire DN within the group. If this property is not set, the default value is `false`
- ldap.groupSearchFilter -- the search filter that should be used when loading groups. If this property is not set, the default value is `("ldap.groupNameField"={0})`
- ldap.searchFields -- the LDAP fields that will be used for user searches. If this property is not set, the username, name, and email fields will be searched. An example value for this field is "Username/uid,Name/cname". That searches the uid and cname fields in the directory and labels them as "Username" and "Name" in the search UI. You can add as many fields as you'd like using comma-delimited "DisplayName/Field" pairs. You should ensure that any fields used for searching are properly indexed so that searches return quickly.

Below is a sample config file section:

        <jive>
          ...
          <ldap>
            <host></host>
            <port>389</port>
            <usernameField>uid</usernameField>
            <nameField>cn</nameField>
            <emailField>mail</emailField>
            <baseDN>ou=People;dc=example;dc=com</baseDN>
            <adminDN>cn=Directory Administrator</adminDN>
            <adminPassword></adminPassword>
          </ldap>
          <provider>
            <user>
              <className>org.jivesoftware.wildfire.ldap.LdapUserProvider</className>
            </user>
            <auth>
              <className>org.jivesoftware.wildfire.ldap.LdapAuthProvider</className>
            </auth>
            <group>
              <className>org.jivesoftware.wildfire.ldap.LdapGroupProvider</className>
            </group>
          </provider>
          ...
        </jive>

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 `admin.authorizedUsernames` property. For example, to let the usersnames "joe" and "jane" login to the admin console:

        <jive>
          ...
          <admin>
            ...
            <authorizedUsernames>joe, jane</authorizedUsernames>
          </admin>
          ...
        </jive>

## Custom Search Filter

By default, Wildfire will load all objects under the baseDN that have the attribute specified by `ldap.usernameField`. In the case that the username field is set to "uid", the search for all users would be "(uid=*)". However, there are cases when this logic does not work -- for example, when a directory contains other objects besides users but all objects share "uid" as a unique identifier field. In that case, you may need to specify a custom search filter using `ldap.searchFilter`. As an example, a search filter for all users with a "uid" and a "cn" value of "joe" would be:

    (&(uid={0})(cn=joe))

The "{0}" value in the filter above is a token that should be present in all custom search filters. It will be dynamically replaced with "*" when loading the list of all users or a username when loading a single user.

Some custom search filters may include reserved XML entities such as "&". In that case, you must enter the search filter into the wildfire.xml file using CDATA:

    <searchFilter><![CDATA[(&(sAMAccountName={0})(|(givenName=GEORGE)(givenName=admin)))]]></searchFilter>

## Custom Inital Context Factory

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 wildfire.xml:

      <ldap>
        ... other ldap settings here
        <initialContextFactory>com.foo.factoryClass</initialContextFactory>
      </ldap>

## Connection Pooling

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 `ldap.connectionPoolEnabled` to `false`:

    <ldap>
            ... other ldap settings here
            <connectionPoolEnabled>false</connectionPoolEnabled>
            </ldap>

You should set several Java system properties to change default pool settings. For more information, see the following pages:

- http://java.sun.com/products/jndi/tutorial/ldap/connect/pool.html
- http://java.sun.com/products/jndi/tutorial/ldap/connect/config.html

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.

## LDAP vCard Integration

The LDAP vCard provider will expose LDAP profile information as vCard data for XMPP clients that support the XMPP vCard extension. First, enable the provider:

        <provider>
          ...
          <vcard>
            <className>org.jivesoftware.wildfire.ldap.LdapVCardProvider</className>
          </vcard>
          ...
        </provider>

Next, you must add mappings between LDAP fields and vCard fields in the wildfire.xml file. The vcard attributes are configured by adding an attrs="attr1,attr2" attribute to the vcard elements. Arbitrary text can be used for the element values as well as MessageFormat style placeholders for the ldap attributes. For example, if you wanted to map the LDAP attribute displayName to the vcard element FN, the XML snippet would be: <FN attrs="displayName">{0}</FN>

The vCard XML must be escaped in CDATA and must also be well formed. It is the exact XML this provider will send to a client after after stripping attr attributes and populating the placeholders with the data retrieved from LDAP. This system should be flexible enough to handle any client's vCard format. An example mapping follows.

        <ldap>
          <vcard-mapping>
            <![CDATA[
              <vCard xmlns='vcard-temp'>
                <FN attrs="displayName">{0}</FN>
                <NICKNAME attrs="uid">{0}</NICKNAME>
                <BDAY attrs="dob">{0}</BDAY>
                <ADR>
                  <HOME/>
                  <EXTADR>Ste 500</EXTADR>
                  <STREET>317 SW Alder St</STREET>
                  <LOCALITY>Portland</LOCALITY>
                  <REGION>Oregon</REGION>
                  <PCODE>97204</PCODE>
                  <CTRY>USA</CTRY>
                </ADR>
                <TEL>
                  <HOME/>
                  <VOICE/>
                  <NUMBER attrs="telephoneNumber">{0}</NUMBER>
                </TEL>
                <EMAIL>
                <INTERNET/>
                <USERID attrs="mail">{0}</USERID>
                </EMAIL>
                <TITLE attrs="title">{0}</TITLE>
                <ROLE attrs="">{0}</ROLE>
                <ORG>
                  <ORGNAME attrs="o">{0}</ORGNAME>
                  <ORGUNIT attrs="">{0}</ORGUNIT>
                </ORG>
                <URL attrs="labeledURI">{0}</URL>
                <DESC attrs="uidNumber,homeDirectory,loginShell">
                uid: {0} home: {1} shell: {2}
                </DESC>
              </vCard>
            ]]>
          </vcard-mapping>
        </ldap>

## LDAP FAQ

Can I create new users through Wildfire when using LDAP?

No, Wildfire treats LDAP directories as read-only. Therefore, it's not possible to create or edit users through the application.

Why is the list of usernames not sorted in the admin console when using LDAP?

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. However, you can enable client-side sorting of search results by setting `ldap.clientSideSorting` to true in the XML configuration file.

I switched to LDAP and now cannot login to the admin console. What happened?

If you can no longer login to the admin console after switching, one of two things most likely happened:

1. 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).
2. 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.

You can also enable debugging to get more information from the LDAP module. To do this, add <log><debug><enabled>true</enabled></debug></log> to your `conf/wildfire.xml` file. Log statements will be written to the `logs/debug.log` file.