<chapter id="ldap-integration">
<title>LDAP Integration</title>
<para>
OTRS is capable of using an LDAP directory as source in several places. The following HowTo describes the use of an Active Directory server together with OTRS. The example may serve for the basic understanding of binding against an LDAP server, too.
</para>

<section id="ldap-integration-ad">
<title>Active Directory</title>
<para>
This section will explain how to integrate OTRS with Microsoft's Active Directory using its LDAP features to provide authentication for both helpdesk staff and users, as well as information for the customer user backend.
</para> 
<para> 
We make the assumption that OTRS is running on a Linux server and that you know how to work with Microsoft Active Directory and its admin tools.
</para>
<para> 
Naming of particular products or brands should not be seen as endorsements. 
</para>
<para> 
You are strongly recommended to take a backup of your system before major installation and backups at regular intervals. 
</para> 

<section id="ldap-integration-ad-preparations">
<title>Preparations</title>

<para> 
The only non standard requirement for the OTRS server is that the Perl module Net::LDAP is installed. This module can be downloaded from http://ldap.perl.org/
</para> 
<para> 
It is also a good idea to first create a user in OTRS using the standard DB authentication mechanism. If you configure LDAP/AD authentication before you have created any users, you won't be able to log on. Create a user that you give admin rights to and which corresponds to the username listed in Active Directory.
</para> 
<para> 
In order to use the LDAP facility of OTRS against Windows Active Directory, one would have to have at least one Active Directory (hereby referred to as AD) Controller. The AD controller must also be a Global Catalog server in order to allow authentication against the AD tree.
</para> 
<para> 
One can use Active Directory running on both Windows 2000 and Windows 2003.
</para> 
<para> 
Details needed during configuration of OTRS and LDAP.
</para>
<itemizedlist>
<listitem><para>Hostname of your AD server</para></listitem>
<listitem><para>Base DN of your Active Directory tree</para></listitem>
<listitem><para>Username and password that will handle LDAP lookups (AD does not allow anonymous LDAP lookups)</para></listitem>
</itemizedlist>

</section>

<section id="ldap-integration-ad-config">
<title>Configuration</title>

<section id="ldap-integration-ad-config-windows">
<title>Configuration of Active Directory</title>
<para>
Create a new user in AD using "AD Users and Computers". The user will only need to be a normal user with "Domain User" membership.
If a domain policy exists which enforces a password change on a regular basis, configure this users Account settings to "Password Never Expires".
</para>
</section>

<section id="ldap-integration-ad-config-otrs">
<title>OTRS LDAP options</title>

<para>
OTRS provides three different ways of using LDAP.
</para>
<itemizedlist>
<listitem><para>Authenticate Agent users</para></listitem>
<listitem><para>Authenticate Customer users</para></listitem>
<listitem><para>Get customer User information</para></listitem>
</itemizedlist>
<para>
The two customer modules are best used together, or a lot of manual work is needed. This howto will only cover the two used together. 
</para>

</section>

</section>

<section id="ldap-integration-ad-agent-auth">
<title>Agent Authentication</title>
<para>
Use your favorite editor to edit the Config.pm file of your OTRS installation. This file is usually located at &lt;OTRS_HOME&gt;/Kernel/Config.pm.
</para>
<para>
Start by adding:
</para>
<example>
<title>Kernel/Config.pm - AuthModule</title>
<programlisting>
   $Self->{'AuthModule'} = 'Kernel::System::Auth::LDAP';
</programlisting>
</example>
<para>
This will change the authentication module and use LDAP.
</para>
<para>
Next, add the following three lines:
</para>
<example>
<title>Kernel/Config.pm - AuthModule::LDAP settings</title>
<programlisting>
   $Self->{'AuthModule::LDAP::Host'} = '[AD_server]'; 
   $Self->{'AuthModule::LDAP::BaseDN'} = '[base_dn]';
   $Self->{'AuthModule::LDAP::UID'} = 'sAMAccountName';
</programlisting>
</example>
<para>
Replace [AD_server] with the hostname or IP address of your AD domain controller.. i.e.: w2kad.example.com.
</para>
<para>
Replace [base_dn] with the Distinguish Name of your AD forest. This follows the syntax of "dc=example, dc=com".
</para>
<para>
An easy way to find the [base_dn] is by looking at the name which "AD Users and Computers" shows when you view the AD tree structure. It will show the domainname as "example.com" or "subdomain.example.com" according to how your AD forest was first set up. To transfer that to a Base DN syntax, the above examples will become "dc=example, dc=com" and "dc=subdomain, dc=example, dc=com".
</para>
<para>
If you still can't find your base DN of your AD forest, you can use tools like LDIFDE (found on your AD controller) or ADSI Edit (found on the W2K Support Kit CD/Resource Kit CD).
</para>
<para>
As Active Directory uses a different account object than a normal LDAP tree, we have to use specify "sAMAccountName" for the UID object throughout the configuration.
</para>
<para>
Next you will need to specify the login account used to communicate with the AD. This is done by configuring the following settings:</para>
<example>
<title>Kernel/Config.pm - AuthModule::LDAP::SearchUser settings</title>
<programlisting>
  $Self->{'AuthModule::LDAP::SearchUserDN'} = '[user_dn]';
  $Self->{'AuthModule::LDAP::SearchUserPw'} = '[password]';
</programlisting>
</example>
<para>
Replace [user_dn] with the full DN of your selected AD/OTRS config user. Follow standard LDAP syntax. The full DN will differ depending on which container you have added the user using "AD Users and Computers". If you placed it in the existing "Users" container, the syntax will be "cn=FirstName Lastname, cn=Users, [base_dn]". If one is unsure about the complete DN for this user, run LDIFDE.EXE on your AD controller and search for the user.
</para>
<para>
Replace [password] with the password you selected for this account.
</para>

</section>

<section id="ldap-integration-ad-customer-auth">
<title>Customer Authentication</title>
<para>
The configuration of the authentication module for customers (end users) is specified by the following parameters:
</para>
<example>
<title>Kernel/Config.pm - Customer::AuthModule LDAP settings</title>
<programlisting>
  $Self->{'Customer::AuthModule'} = 'Kernel::System::CustomerAuth::LDAP';
  $Self->{'Customer::AuthModule::LDAP::Host'} = '[AD_server]';
  $Self->{'Customer::AuthModule::LDAP::BaseDN'} = '[base_dn]';
  $Self->{'Customer::AuthModule::LDAP::UID'} = 'sAMAccountName';
  $Self->{'Customer::AuthModule::LDAP::SearchUserDN'} = '[user_dn]';
  $Self->{'Customer::AuthModule::LDAP::SearchUserPw'} = '[password]';
</programlisting>
</example>
<para>
Replace [AD_server] with the full hostname or IP address of your AD domain controller.. i.e.: w2kad.example.com.
</para>
<para>
Replace [base_dn] with the DN of where your customer users are location in the AD tree. If all customer users are located  under a "People" container, specify that as the base_dn for Customer Auth, ie. "ou=People, dc=example, dc=com". If you have users in multiple containers, you may want to add multiple LDAP sources. Feel free to use $Self->{CustomerUser1} throughout $Self->{CustomerUser10}.
</para>
<para>
Replace [user_dn] with your selected OTRS/AD user and [password] with its corresponding password.
</para>
</section>

<section id="ldap-integration-ad-customer-data">
<title>Get Customer User Information</title>
<para>
The customer user information configuration consists of a configuration setting similar to this:
</para>
<example>
<title>Kernel/Config.pm - CustomerUser::LDAP settings</title>
<programlisting>
  $Self->{CustomerUser} = {
    Module => 'Kernel::System::CustomerUser::LDAP',
    Params => {
      Host => '[AD_server]',
      BaseDN => '[base_dn]',
      SSCOPE => 'sub',
      UserDN => '[user_dn]',
      UserPw => '[password]',
    },
    CustomerKey => 'sAMAccountName',
    CustomerID => '[customer_id]',
    CustomerUserListFields => ['sAMAccountName', 'cn', 'mail'],
    CustomerUserSearchFields => ['sAMAccountName', 'cn', 'mail'],
    CustomerUserPostMasterSearchFields => ['mail'],
    CustomerUserNameFields => ['givenname', 'sn'],
    Map => [
      # note: Login, Email and CustomerID needed!
      # var, frontend, storage, shown, required, storage-type
#       [ 'UserSalutation', 'Title', 'title', 1, 0, 'var' ],
      [ 'UserFirstname', 'Firstname', 'givenname', 1, 1, 'var' ],
      [ 'UserLastname', 'Lastname', 'sn', 1, 1, 'var' ],
      [ 'UserLogin', 'Login', 'sAMAccountName', 1, 1, 'var' ],
      [ 'UserEmail', 'Email', 'mail', 1, 1, 'var' ],
      [ 'UserCustomerID', 'CustomerID', 'mail', 0, 1, 'var' ],
#       [ 'UserPhone', 'Phone', 'telephonenumber', 1, 0, 'var' ],
#       [ 'UserAddress', 'Address', 'postaladdress', 1, 0, 'var' ],
#       [ 'UserComment', 'Comment', 'description', 1, 0, 'var' ],
    ],
  };
</programlisting>
</example>
<para>
Replace [AD_server] with the name or IP address of your AD controller.
</para>
<para>
Replace [base_dn] with the location of your users using LDAP syntax, ie. "ou=People, dc=example, dc=com".
</para>
<para>
Replace [user_dn] and [password] as usual.
</para>
<para>
Replace [customer_id] with the id you want to show your users as: A usual selection here is 'mail' or 'o'.
</para>
<para>
CustomerUserListFields can be changed to specify how you want the Customers listed when creating/working tickets.  With the default setting above, the customer will be listed as: "username Lastname email@address"
</para>
<para>
If you want to only list email addresses, just remove the other two entries and leave "'mail'". You can also add other fields to this list if you know the LDAP attribute name of the fields.
</para>
<para>
CustomerUserSearchFields has the same options as CustomerUserListFields. When creating a new ticket and using the "Search Customer" function, the listed fields will be searched according to your criteria. Add the attribute 'o' here to allow entering an organization's name to list all customer user from that organization, provided the information is present in LDAP/AD.
</para>

</section>

<section id="ldap-integration-ad-tnt">
<title>Tips &amp; Tricks</title>

<section id="ldap-integration-ad-tnt-groupdn">
<title>Control who's let in</title>
<para>
Prevent customer users and agents from logging on to the front-end when you have a large AD structure where agents and end users are located in the same OU's. You do so by creating a posixGroup somewhere and fill it with either the UID or the DN of the wanted members.
</para>
<example>
<title>Kernel/Config.pm - GroupDN for Agents</title>
<programlisting>
  $Self->{'AuthModule::LDAP::GroupDN'} = 'cn=otrsallow_A, ou=posixGroups, dc=example, dc=com';
  $Self->{'AuthModule::LDAP::AccessAttr'} = 'memberUid';
 #$Self->{'AuthModule::LDAP::UserAttr'} = 'UID';
  $Self->{'AuthModule::LDAP::UserAttr'} = 'DN';
</programlisting>
</example>
<example>
<title>Kernel/Config.pm - GroupDN for Customers</title>
<programlisting>
  $Self->{'Customer::AuthModule::LDAP::GroupDN'} = 'cn=otrsallow_C, ou=posixGroups, dc=example, dc=com';
  $Self->{'Customer::AuthModule::LDAP::AccessAttr'} = 'memberUid';
 #$Self->{'Customer::AuthModule::LDAP::UserAttr'} = 'UID';
  $Self->{'Customer::AuthModule::LDAP::UserAttr'} = 'DN';
</programlisting>
</example>
</section>

<section id="ldap-integration-ad-tnt-example">
<title>Config Example</title>
<para>
This is a complete example of how OTRS can be configured to authenticate both agents and customers, as well as provide info on customers via LDAP. The database settings are maintained.
</para>
<para>
Settings used:
</para>
<itemizedlist>
<listitem><para>
Windows AD Controller: w2kad.example.com
</para></listitem>
<listitem><para>
Windows OTRS Lookup User: otrs (Full Name: OTRS Service)
</para></listitem>
<listitem><para>
Windows OTRS user Password: secret
</para></listitem>
<listitem><para>
Windows OTRS Lookup user DN: cn=OTRS Service, ou=People, dc=example, dc=com
</para></listitem>
<listitem><para>
Windows Domain: example.com
</para></listitem>
<listitem><para>
Windows LDAP Base DN: dc=example, dc=com
</para></listitem>
<listitem><para>
Windows Users Container: People
</para></listitem>
<listitem><para>
Windows Users LDAP DN: ou=People, dc=example, dc=com
</para></listitem>
<listitem><para>
Windows Agents LDAP DN: ou=People, dc=example, dc=com
</para></listitem>

</itemizedlist>

<example>
<title>Kernel/Config.pm - AuthModule LDAP settings</title>
<programlisting>
  #------------------------------------------------------------------------
  # Start of Example Config
  $Self->{'AuthModule'} = 'Kernel::System::Auth::LDAP';
  $Self->{'AuthModule::LDAP::Host'} = 'w2kad.example.com';
  $Self->{'AuthModule::LDAP::BaseDN'} = 'dc=example, dc=com';
  $Self->{'AuthModule::LDAP::UID'} = 'sAMAccountName';

  $Self->{'AuthModule::LDAP::SearchUserDN'} = '';
  $Self->{'AuthModule::LDAP::SearchUserPw'} = 'secret';


  # This is an example configuration for an LDAP auth. backend.
  # (take care that Net::LDAP is installed!)
  $Self->{'Customer::AuthModule'} = 'Kernel::System::CustomerAuth::LDAP';
  $Self->{'Customer::AuthModule::LDAP::Host'} = 'w2kad.example.com';
  $Self->{'Customer::AuthModule::LDAP::BaseDN'} = 'ou=People, dc=example, dc=com';
  $Self->{'Customer::AuthModule::LDAP::UID'} = 'sAMAccountName';

  # The following is valid but would only be necessary if the
  # anonymous user do NOT have permission to read from the LDAP tree
  $Self->{'Customer::AuthModule::LDAP::SearchUserDN'} = 'cn=OTRS Service, ou=People, dc=example, dc=com';
  $Self->{'Customer::AuthModule::LDAP::SearchUserPw'} = 'secret';

  # CustomerUser
  # (customer user database backend and settings)
    $Self->{CustomerUser} = {
        Name => 'Datenbank',
        Module => 'Kernel::System::CustomerUser::DB',
        Params => { Table => 'customer_user',
            # to use an external database
#           DSN => 'DBI:odbc:yourdsn',
#           DSN => 'DBI:mysql:database=customerdb;host=customerdbhost',
#           User => '', Password => '',
        },
        # customer uniq id
        CustomerKey => 'login',
        CustomerID => 'customer_id',
        CustomerValid => 'valid_id',
        CustomerUserListFields => ['first_name', 'last_name', 'email'],
#       CustomerUserListFields => ['login', 'first_name', 'last_name', 'customer_id', 'email'],
        CustomerUserSearchFields => ['login', 'last_name', 'customer_id'],
        CustomerUserSearchPrefix => '',
        CustomerUserSearchSuffix => '*',
        CustomerUserSearchListLimit => 250,
        CustomerUserPostMasterSearchFields => ['email'],
        CustomerUserNameFields => ['salutation', 'first_name', 'last_name'],
#       ReadOnly => 1,
        Map => [
            # note: Login, Email and CustomerID needed!
            # var, frontend, storage, shown, required, storage-type, http-link
            [ 'UserSalutation', 'Salutation', 'salutation', 1, 0, 'var' ],
            [ 'UserFirstname', 'Firstname', 'first_name', 1, 1, 'var' ],
            [ 'UserLastname', 'Lastname', 'last_name', 1, 1, 'var' ],
            [ 'UserLogin', 'Login', 'login', 1, 1, 'var' ],
            [ 'UserPassword', 'Password', 'pw', 0, 1, 'var' ],
            [ 'UserEmail', 'Email', 'email', 0, 1, 'var' ],
            [ 'UserCustomerID', 'CustomerID', 'customer_id', 0, 1, 'var' ],
            [ 'UserComment', 'Comment', 'comments', 1, 0, 'var' ],
            [ 'ValidID', 'Valid', 'valid_id', 0, 1, 'int' ],
        ],
    };

  # CustomerUser1
  # (customer user ldap backend and settings)
  $Self->{CustomerUser1} = {
    Module => 'Kernel::System::CustomerUser::LDAP',
    Params => {
      # ldap host
      Host => 'w2kad.example.com',
      # ldap base dn
      BaseDN => 'ou=People, dc=example, dc=com',
      # search scope (one|sub)
      SSCOPE => 'sub',
      # The following is valid but would only be necessary if the
      # anonymous user does NOT have permission to read from the LDAP tree
      UserDN => 'cn=OTRS Service, ou=People, dc=example, dc=com',
      UserPw => 'secret',
      AlwaysFilter => '',
      SourceCharset => 'utf-8',
      DestCharset => 'iso-8859-1',
    },
    # customer uniq id
    CustomerKey => 'sAMAccountName',
    # customer #
    CustomerID => 'mail',
    CustomerUserListFields => ['sAMAccountName', 'cn', 'mail'],
    CustomerUserSearchFields => ['sAMAccountName', 'cn', 'mail'],
    CustomerUserSearchPrefix => '',
    CustomerUserSearchSuffix => '*',
    CustomerUserSearchListLimit => 250,
    CustomerUserPostMasterSearchFields => ['mail'],
    CustomerUserNameFields => ['givenname', 'sn'],
    Map => [
      # note: Login, Email and CustomerID needed!
      # var, frontend, storage, shown, required, storage-type
      #[ 'UserSalutation', 'Title', 'title', 1, 0, 'var' ],
      [ 'UserFirstname', 'Firstname', 'givenname', 1, 1, 'var' ],
      [ 'UserLastname', 'Lastname', 'sn', 1, 1, 'var' ],
      [ 'UserLogin', 'Login', 'sAMAccountName', 1, 1, 'var' ],
      [ 'UserEmail', 'Email', 'mail', 1, 1, 'var' ],
      [ 'UserCustomerID', 'CustomerID', 'mail', 0, 1, 'var' ],
      [ 'UserPhone', 'Phone', 'telephonenumber', 1, 0, 'var' ],
      #[ 'UserAddress', 'Address', 'postaladdress', 1, 0, 'var' ],
      #[ 'UserComment', 'Comment', 'description', 1, 0, 'var' ],
    ],
  };
  # End example config
  #------------------------------------------------------------------------
</programlisting>
</example>
</section>

</section>

</section>

</chapter>
