Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ldap.xml 54.85 KiB
<?xml version="1.0" encoding="UTF-8"?>
<chapter annotations="slide" version="5.1" xml:id="sdiLdap"
         xmlns="http://docbook.org/ns/docbook"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xmlns:xila="http://www.w3.org/2001/XInclude/local-attributes"
         xmlns:xi="http://www.w3.org/2001/XInclude"
         xmlns:svg="http://www.w3.org/2000/svg"
         xmlns:ns="http://docbook.org/ns/transclusion"
         xmlns:m="http://www.w3.org/1998/Math/MathML"
         xmlns:html="http://www.w3.org/1999/xhtml"
         xmlns:db="http://docbook.org/ns/docbook">
  <title><xref linkend="glo_LDAP"/></title>

  <figure xml:id="sdi_ldap_readings">
    <title>Recommended readings</title>

    <itemizedlist>
      <listitem>
        <para><uri xlink:href="http://www.zytrax.com/books/ldap">LDAP for
        Rocket Scientists</uri></para>
      </listitem>

      <listitem>
        <para><uri
        xlink:href="https://www.novell.com/coolsolutions/feature/15359.html">An
        Introduction to LDAP: Part 1</uri></para>
      </listitem>

      <listitem>
        <para><uri
        xlink:href="http://quark.humbug.org.au/publications/ldap/ldap_tut.html">Introduction
        to LDAP</uri></para>
      </listitem>

      <listitem>
        <para><uri
        xlink:href="http://ldapman.org/articles/intro_to_ldap.html">ldapman.org,
        An Introduction to LDAP</uri></para>
      </listitem>
    </itemizedlist>
  </figure>

  <figure xml:id="sdi_ldap_opendapDoc">
    <title><productname>Openldap</productname> server documentation</title>

    <para>Exercises are based on the <uri
    xlink:href="http://www.openldap.org">OpenLDAP</uri> server
    implementation.</para>

    <para>Related material at <uri
    xlink:href="http://www.openldap.org">http://www.openldap.org</uri>.</para>
  </figure>

  <figure xml:id="sdi_ldap_whatIsLdap">
    <title>What is LDAP anyway?</title>

    <itemizedlist>
      <listitem>
        <para><emphasis role="red">L</emphasis>ightweight <emphasis
        role="red">D</emphasis>irectory <emphasis role="red">A</emphasis>ccess
        <emphasis role="red">P</emphasis>rotocol</para>
      </listitem>

      <listitem>
        <para>Vendor independent</para>
      </listitem>

      <listitem>
        <para><link xlink:href="https://tools.ietf.org/html/rfc4511">IETF
        standard</link>:</para>

        <blockquote>
          <para>Clients interact with servers using a directory access
          protocol</para>
        </blockquote>
      </listitem>
    </itemizedlist>
  </figure>

  <figure xml:id="sdi_ldap_bind">
    <title>LDAP Server cli bind</title>

    <informaltable border="1" width="100%">
      <colgroup width="50%"/>

      <colgroup width="50%"/>

      <tr>
        <th>Command</th>

        <th>Result</th>
      </tr>

      <tr>
        <td valign="top"><screen><command
              xlink:href="https://linux.die.net/man/1/ldapsearch">ldapsearch</command> \
  -h localhost <co linkends="sdi_ldap_bind-1.2" xml:id="sdi_ldap_bind-1.2-co"/> \
  -D "cn=admin,dc=betrayer,dc=com" <co linkends="sdi_ldap_bind-2.2"
              xml:id="sdi_ldap_bind-2.2-co"/>\
  -w password -x <co linkends="sdi_ldap_bind-3.2"
              xml:id="sdi_ldap_bind-3.2-co"/>\
  -b "dc=betrayer,dc=com" <co linkends="sdi_ldap_bind-4.2"
              xml:id="sdi_ldap_bind-4.2-co"/>\
  -s sub <co linkends="sdi_ldap_bind-5.2" xml:id="sdi_ldap_bind-5.2-co"/> \
  -LLL <co linkends="sdi_ldap_bind-6.2" xml:id="sdi_ldap_bind-6.2-co"/></screen></td>

        <td valign="top"><screen>dn: dc=betrayer,dc=com <co
              linkends="sdi_ldap_bind-7" xml:id="sdi_ldap_bind-7-co"/>
objectClass: top
objectClass: dcObject
objectClass: organization
o: Betrayers heaven <co linkends="sdi_ldap_bind-8" xml:id="sdi_ldap_bind-8-co"/>
dc: betrayer 

dn: cn=admin,dc=betrayer,dc=com <co linkends="sdi_ldap_bind-9"
              xml:id="sdi_ldap_bind-9-co"/>
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin <co linkends="sdi_ldap_bind-10" xml:id="sdi_ldap_bind-10-co"/>
description: LDAP administrator
userPassword:: e1NT...dE53N1E= <co linkends="sdi_ldap_bind-11"
              xml:id="sdi_ldap_bind-11-co"/></screen></td>
      </tr>
    </informaltable>

    <informaltable role="slideExclude" width="100%">
      <colgroup width="50%"/>

      <colgroup width="50%"/>

      <tr>
        <td valign="top"><calloutlist role="slideExclude">
            <callout arearefs="sdi_ldap_bind-1.2-co"
                     xml:id="sdi_ldap_bind-1.2">
              <para>An <xref linkend="glo_LDAP"/> server's <xref
              linkend="glo_DNS"/> name or IP address (<emphasis
              role="bold">h</emphasis>ost).</para>
            </callout>

            <callout arearefs="sdi_ldap_bind-2.2-co"
                     xml:id="sdi_ldap_bind-2.2">
              <para>The bind <xref linkend="glo_DN"/>. This path is being
              required to uniquely identify an existent user entry on the
              server in question. This corresponds to a login name when using
              <abbrev>e.g.</abbrev> web <acronym>gui</acronym>
              authentication.</para>
            </callout>

            <callout arearefs="sdi_ldap_bind-3.2-co"
                     xml:id="sdi_ldap_bind-3.2">
              <para>The corresponding password to the given bind <xref
              linkend="glo_DN"/>. The user entry must contain a corresponding
              userPassword hash value. The <option>-x</option> indicates using
              simple password based rather than <acronym
              xlink:href="https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer">SASL</acronym>
              authentication.</para>
            </callout>

            <callout arearefs="sdi_ldap_bind-4.2-co"
                     xml:id="sdi_ldap_bind-4.2">
              <para>The search will start from this uniquely defined node
              within the servers <xref linkend="glo_DIT"/>. In the current
              example we simply choose the tree's top level node.</para>
            </callout>

            <callout arearefs="sdi_ldap_bind-5.2-co"
                     xml:id="sdi_ldap_bind-5.2">
              <para>The search scope. See <link
              xlink:href="https://ldap.com/the-ldap-search-operation">The LDAP
              Search Operation</link> for details.</para>
            </callout>

            <callout arearefs="sdi_ldap_bind-6.2-co"
                     xml:id="sdi_ldap_bind-6.2">
              <para>Suppress verbose search information.</para>
            </callout>
          </calloutlist></td>

        <td valign="top"><calloutlist role="slideExclude">
            <callout arearefs="sdi_ldap_bind-7-co" xml:id="sdi_ldap_bind-7">
              <para>The <xref linkend="glo_DIT"/>'s top level node.</para>
            </callout>

            <callout arearefs="sdi_ldap_bind-8-co" xml:id="sdi_ldap_bind-8">
              <para>Your organization's name</para>
            </callout>

            <callout arearefs="sdi_ldap_bind-9-co" xml:id="sdi_ldap_bind-9">
              <para>The administrative user of the server. This entry will
              typically be created at installation time.</para>
            </callout>

            <callout arearefs="sdi_ldap_bind-10-co" xml:id="sdi_ldap_bind-10">
              <para>The administrative user's <quote>common name</quote>. Note
              this entry corresponds to <coref
              linkend="sdi_ldap_bind-4.2-co"/> on the <quote>Command</quote>
              side of this table.</para>
            </callout>

            <callout arearefs="sdi_ldap_bind-11-co" xml:id="sdi_ldap_bind-11">
              <para>The administrative user password's hash value. Note the
              double colon <code>::</code> indicating a base64 encoded binary
              value.</para>
            </callout>
          </calloutlist></td>
      </tr>
    </informaltable>
  </figure>

  <figure xml:id="sdi_ldap_Dit">
    <title>Document Information Tree (<abbrev>DIT</abbrev>)</title>

    <mediaobject>
      <imageobject>
        <imagedata fileref="Fig/dit.multi.svg"/>
      </imageobject>
    </mediaobject>
  </figure>

  <figure xml:id="sdi_ldap_relAbsDn">
    <title><productname>Relative and absolute DNs</productname></title>

    <mediaobject>
      <imageobject>
        <imagedata fileref="Fig/relativeDn.multi.svg"/>
      </imageobject>
    </mediaobject>
  </figure>

  <figure xml:id="sdi_ldap_userExample">
    <title>User example</title>

    <programlisting language="none">dn: <emphasis role="red">uid=clark,ou=finance,dc=betrayer,dc=de</emphasis> <co
        linkends="sdi_ldap_userExample-1" xml:id="sdi_ldap_userExample-1-co"/>
cn: Sandy Clark
homeDirectory: /home/clark
sn: Clark
<emphasis role="red">uid</emphasis>: <emphasis role="red">clark</emphasis> <co
        linkends="sdi_ldap_userExample-2" xml:id="sdi_ldap_userExample-2-co"/>
uidNumber: 21101
givenName: Sandy
loginShell: /bin/bash
<emphasis role="red">mail: clark@betrayer.com</emphasis> <co
        linkends="sdi_ldap_userExample-3" xml:id="sdi_ldap_userExample-3-co"/>
<emphasis role="red">mail: finance@betrayer.com</emphasis>
postOfficeBox: 10G
userPassword: {SSHA}noneOfYourBusiness</programlisting>

    <calloutlist role="slideExclude">
      <callout arearefs="sdi_ldap_userExample-1-co"
               xml:id="sdi_ldap_userExample-1">
        <para>The entry's absolute distinguished name (<xref
        linkend="glo_DN"/>). This name/value list uniquely identifies an entry
        (an its position) within a given <xref linkend="glo_DIT"/>.</para>
      </callout>

      <callout arearefs="sdi_ldap_userExample-2-co" role="slideExclude"
               xml:id="sdi_ldap_userExample-2">
        <para>This key/value combination is guaranteed to be unique within
        respect to the given parent node. It allows to identify each node with
        respect to its parent. So in a relational model the
        <quote><code>clark</code></quote> entry would become a weak entity
        having an identifying ownership relation to its parent organisational
        unit by virtue of its <code>uid</code> value.</para>

        <para>In other words: There is only one such entry below <emphasis
        role="red">ou=finance,dc=betrayer,dc=de</emphasis> having an <emphasis
        role="red">uid</emphasis> attribute of value <emphasis
        role="red">clark</emphasis>.</para>
      </callout>

      <callout arearefs="sdi_ldap_userExample-3-co"
               xml:id="sdi_ldap_userExample-3">
        <para><xref linkend="glo_LDAP"/> allows for multi valued
        attributes.</para>
      </callout>
    </calloutlist>
  </figure>

  <figure xml:id="sdi_ldap_objectClasses">
    <title>objectClass</title>

    <itemizedlist>
      <listitem>
        <para>Structuring <xref linkend="glo_LDAP"/> entry data.</para>
      </listitem>

      <listitem>
        <para>Categories:</para>

        <itemizedlist>
          <listitem>
            <para>Structural</para>
          </listitem>

          <listitem>
            <para>Auxiliary</para>
          </listitem>

          <listitem>
            <para>Abstract</para>
          </listitem>
        </itemizedlist>
      </listitem>
    </itemizedlist>
  </figure>

  <figure xml:id="sdi_ldap_objectClassesClarification">
    <title>objectClass clarifications</title>

    <informaltable border="0">
      <tr>
        <td valign="top"><glosslist>
            <glossentry>
              <glossterm>Abstract classes:</glossterm>

              <glossdef>
                <para>To be extended by other classes</para>
              </glossdef>
            </glossentry>

            <glossentry>
              <glossterm>Structural classes:</glossterm>

              <glossdef>
                <itemizedlist>
                  <listitem>
                    <para>Each entry requires exactly one.</para>
                  </listitem>

                  <listitem>
                    <para>Specify the <quote>main</quote> type of
                    object.</para>
                  </listitem>

                  <listitem>
                    <para>Must not inherit from auxiliary classes.</para>
                  </listitem>
                </itemizedlist>
              </glossdef>
            </glossentry>
          </glosslist></td>

        <td valign="top"><glosslist>
            <glossentry>
              <glossterm>Auxiliary classes:</glossterm>

              <glossdef>
                <itemizedlist>
                  <listitem>
                    <para>Provide non-conflicting supplementary
                    information.</para>
                  </listitem>

                  <listitem>
                    <para>Think of (<xref linkend="glo_Java"/>)
                    interfaces.</para>
                  </listitem>

                  <listitem>
                    <para>Must not inherit from structural classes.</para>
                  </listitem>
                </itemizedlist>
              </glossdef>
            </glossentry>
          </glosslist></td>
      </tr>
    </informaltable>
  </figure>

  <figure xml:id="sdi_ldap_auxiliaryExample">
    <title>Augmenting <classname>inetOrgPerson</classname> by
    <classname>posixAccount</classname></title>

    <programlisting language="none">Class                       |   Instance <emphasis
        role="red">uid=clark,ou=finance,dc=betrayer,dc=de</emphasis>
----------------------------+---------------------------------------------------
inetOrgPerson (structural)  |                   
   sn                       |    sn: Clark
   cn                       |    cn: Sandy Clark
  ...                       |     <emphasis role="red">▲</emphasis>
                            |     <emphasis role="red">┃</emphasis>
posixAccount (auxiliary)    |     <emphasis role="red">┃</emphasis>
  cn                        |   <emphasis role="red">see above</emphasis> <co
        linkends="sdi_ldap_auxiliaryExample-1"
        xml:id="sdi_ldap_auxiliaryExample-1-co"/>
  gidNumber                 |   gidNumber: 23113
  homeDirectory             |   homeDirectory: /home/clark
  uid                       |   uid: clark
  uidNumber                 |   uidNumber: 21101
  userPassword              |   userPassword: {SSHA}noneOfYourBusiness
                          .....</programlisting>

    <calloutlist role="slideExclude">
      <callout arearefs="sdi_ldap_auxiliaryExample-1-co"
               xml:id="sdi_ldap_auxiliaryExample-1">
        <para>The <property>cn</property> attribute is being defined both in
        object class <classname>inetOrgPerson</classname> and
        <classname>posixAccount</classname>. This requires data type
        definitions to be compatible.</para>
      </callout>
    </calloutlist>
  </figure>

  <figure xml:id="sdi_ldap_structuralObjectClasses">
    <title>Structural objectClass definitions</title>

    <mediaobject>
      <imageobject>
        <imagedata fileref="Fig/structuralOcInherit.multi.svg"/>
      </imageobject>
    </mediaobject>
  </figure>

  <figure xml:id="sdi_ldap_filter">
    <title>Search scopes</title>

    <para><link xlink:href="https://tools.ietf.org/html/rfc4520">RFC
    4520</link> defines <link
    xlink:href="https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xhtml#ldap-parameters-9">three
    <acronym>LDAP</acronym> search scopes</link>:</para>

    <itemizedlist>
      <listitem>
        <para><link
        xlink:href="https://ldapwiki.com/wiki/BaseObject">baseObject</link>
        (<code>base</code>)</para>
      </listitem>

      <listitem>
        <para><link
        xlink:href="https://ldapwiki.com/wiki/SingleLevel">singleLevel</link>
        (<code>one</code>)</para>
      </listitem>

      <listitem>
        <para><link
        xlink:href="https://ldapwiki.com/wiki/WholeSubtree">wholeSubtree</link>
        (sub)</para>
      </listitem>
    </itemizedlist>
  </figure>

  <figure xml:id="sdi_ldap_filterPredicates">
    <title>Predicate based queries</title>

    <para><link xlink:href="https://tools.ietf.org/html/rfc4520">RFC
    4520</link> defines <link
    xlink:href="https://ldapwiki.com/wiki/LDAP%20Filter%20Choices">predicate
    based queries</link> using <link
    xlink:href="https://en.wikipedia.org/wiki/Reverse_Polish_notation">RPN</link>
    style:</para>

    <itemizedlist>
      <listitem>
        <para><code>(| (cn=k*) (uidNumber &lt; 2000))</code></para>
      </listitem>
    </itemizedlist>
  </figure>

  <figure xml:id="sdi_ldap_bindTypes">
    <title>LDAP bind types</title>

    <itemizedlist>
      <listitem>
        <para>Anonymous bind: No user credentials.</para>

        <para>Note: This typically provides limited privileges.</para>
      </listitem>

      <listitem>
        <para>Simple bind: User's <xref linkend="glo_DN"/> + password:</para>

        <programlisting language="none">DN: <emphasis role="red">uid=clark,ou=finance,dc=betrayer,dc=de</emphasis>
password: <emphasis role="red">123456789</emphasis></programlisting>
      </listitem>
    </itemizedlist>
  </figure>

  <figure xml:id="sdi_ldap_ldif">
    <title>LDIF exchange format</title>

    <itemizedlist>
      <listitem>
        <para><emphasis role="red">L</emphasis>dap <emphasis
        role="red">D</emphasis>ata <emphasis role="red">I</emphasis>nterchange
        <emphasis role="red">F</emphasis>ormat.</para>
      </listitem>

      <listitem>
        <para>Importing and exporting <xref linkend="glo_LDAP"/> Data.</para>
      </listitem>

      <listitem>
        <para>Modifying existing entries (CRUD operations).</para>
      </listitem>

      <listitem>
        <para>Pure <xref linkend="glo_ASCII"/>.</para>
      </listitem>
    </itemizedlist>
  </figure>

  <figure xml:id="sdi_ldap_ldifSample">
    <title>LDIF sample</title>

    <programlisting language="none">dn: uid=clark,ou=finance,dc=betrayer,dc=de
objectClass: posixAccount
objectClass: inetOrgPerson
cn: Sandy Clark
homeDirectory: /home/clark
sn: Clark
uid: clark 
uidNumber: 21101
givenName: Sandy
loginShell: /bin/bash
mail: clark@betrayer.com 
mail: finance@betrayer.com
postOfficeBox: 10G
userPassword: {SSHA}noneOfYourBusiness</programlisting>
  </figure>

  <figure xml:id="sdi_ldap_ditConfigDb">
    <title>OpenLdap server architecture</title>

    <mediaobject>
      <imageobject>
        <imagedata fileref="Fig/openldapArch.multi.svg"/>
      </imageobject>
    </mediaobject>

    <itemizedlist role="slideExclude">
      <listitem>
        <para>An OpenLdap server may host multiple <xref linkend="glo_DIT"/>s
        each being represented by its own database backend.</para>
      </listitem>

      <listitem>
        <para>The server's configuration is itself being stored as a separate
        tree.</para>
      </listitem>

      <listitem>
        <para>Each tree refers to a separate database backend. Thus the above
        example featuring two trees is being implemented by two database
        instances.</para>
      </listitem>
    </itemizedlist>
  </figure>

  <section xml:id="ldapPrepare">
    <title>Recommended Preparations</title>

    <para>The following questions might arise when starting practical
    work:</para>

    <itemizedlist>
      <listitem>
        <para>What is the <acronym>LDAP</acronym> Protocol? What is the
        difference between the two protocols <acronym>ldap</acronym> and
        <acronym>ldaps </acronym>?</para>
      </listitem>

      <listitem>
        <para>What does the acronym <acronym>dc</acronym> in
        <acronym>dc=somedomain, dc=org</acronym> stand for?</para>
      </listitem>

      <listitem>
        <para>What is the role of <acronym>LDAP</acronym>
        <property>objectclass</property> definitions? How do they relate to
        <acronym>LDAP</acronym> schema definitions?</para>
      </listitem>

      <listitem>
        <para>Describe the relationship between <acronym>LDAP</acronym>
        entries and <code>objectClass</code> values.</para>
      </listitem>

      <listitem>
        <para>Is it possible to dynamically change an entries
        structure?</para>
      </listitem>

      <listitem>
        <para>What does the term <quote>bind to an
        <acronym>LDAP</acronym></quote> server mean? What is an
        <quote>anonymous</quote> bind?</para>
      </listitem>

      <listitem>
        <para>Do <acronym>LDAP</acronym> servers in general support database
        features like transactions, ACID semantic etc.?</para>
      </listitem>

      <listitem>
        <para>Explain the term <quote>replication</quote> in an
        <acronym>LDAP</acronym> server context.</para>
      </listitem>

      <listitem>
        <para>Why do organizations sometimes prefer <acronym>LDAP</acronym>
        data repositories rather than using relational database
        systems?</para>
      </listitem>

      <listitem>
        <para>How is the <acronym>LDIF</acronym> format being organized?
        Explain the practical use of <acronym>LDIF</acronym> data when running
        a <acronym>LDAP</acronym> service.</para>
      </listitem>

      <listitem>
        <para><acronym>LDAP</acronym> filters</para>

        <itemizedlist>
          <listitem>
            <para>How do <acronym>LDAP</acronym> filters work?</para>
          </listitem>

          <listitem>
            <para>What is the meaning of the term <emphasis>scope</emphasis>
            ?</para>
          </listitem>

          <listitem>
            <para>How do predicate based filters connected by logical
            <emphasis role="bold">and/or/not</emphasis> look like?</para>
          </listitem>
        </itemizedlist>
      </listitem>

      <listitem>
        <para><productname>OpenLDAP</productname> server software specific
        questions</para>

        <itemizedlist>
          <listitem>
            <para>What does the term <quote>database backend</quote> refer to
            with respect to <productname>OpenLDAP</productname> server
            implementation?</para>
          </listitem>

          <listitem>
            <para>Why is <acronym>LDAP</acronym> replication important?</para>
          </listitem>

          <listitem>
            <para>How do you restrict access to <acronym>LDAP</acronym>
            directories?</para>
          </listitem>

          <listitem>
            <para>How do you speed up predicate based queries?</para>
          </listitem>
        </itemizedlist>
      </listitem>
    </itemizedlist>
  </section>

  <section xml:id="ldapExercises">
    <title>Exercises</title>

    <section xml:id="sdiBrowseExistingLdap">
      <title>Browse an existing <xref linkend="glo_LDAP"/> Server</title>

      <para>Reading data from an existing server requires a suitable client
      software. We recommend using <productname
      xlink:href="https://directory.apache.org/studio">Apache Directory
      Studio</productname>.</para>

      <itemizedlist>
        <listitem>
          <para>Setup Apache Directory Studio to anonymously connect to
          <code>ldap1.hdm-stuttgart.de</code> using <xref linkend="glo_TLS"/>.
          Depending on your location this may require <link
          xlink:href="https://wiki.mi.hdm-stuttgart.de/doku.php?id=studium:infrastruktur:vpn">VPN</link>.</para>
        </listitem>

        <listitem>
          <para>Browse the <xref linkend="glo_DIT"/>.</para>
        </listitem>

        <listitem>
          <para>Use a filter like <code>(uid=xy234)</code> to find your
          personal entry beneath
          <code>ou=userlist,dc=hdm-stuttgart,dc=de</code>. Use the
          corresponding <xref linkend="glo_DN"/> <abbrev>e.g.</abbrev>
          <code>uid=xy234, ou=userlist,dc=hdm-stuttgart,dc=de</code> to
          reconnect using password authentication. Then browse your own entry
          again. Can you spot any difference?</para>
        </listitem>

        <listitem>
          <para>Repeat the previous steps by using the command line
          <command>ldapsearch</command> utility.</para>
        </listitem>
      </itemizedlist>
    </section>
    <section xml:id="ldapServerSetup">
      <title>Set up an <productname>OpenLdap</productname> server</title>

      <itemizedlist>
        <listitem>
          <para><link
          xlink:href="https://ubuntu.com/server/docs/service-ldap">OpenLDAP
          Server</link> provides a good introduction how to install and
          configure OpenLdap.</para>
        </listitem>

        <listitem>
          <para><link
          xlink:href="http://krams915.blogspot.de/2011/01/ldap-apache-directory-studio-basic.html">http://krams915.blogspot.de/2011/01/ldap-apache-directory-studio-basic.html</link>
          provides some more details on populating your server with
          data.</para>
        </listitem>

        <listitem>
          <para>See <link
          xlink:href="http://chee-yang.blogspot.de/2012/03/ldap-introduction-to-openldap.html">http://chee-yang.blogspot.de/2012/03/ldap-introduction-to-openldap.html</link>
          for enabling OpenLdap server configuration by e.g. Apache Directory
          Studio.</para>
        </listitem>
      </itemizedlist>

      <tip>
        <orderedlist>
          <listitem>
            <para>Depending on your systems installation state you may want to
            add the dialog package which allows for feeding additional
            parameters during package installations (e.g. admin's credentials
            and base <xref linkend="glo_DN"/>).</para>
          </listitem>

          <listitem>
            <para>The <link
            xlink:href="https://help.ubuntu.com/lts/serverguide/openldap-server.html#openldap-server-installation">installation
            section</link> hints at your host system defined <xref
            linkend="glo_DNS"/> domain being used for deriving your server's
            <xref linkend="glo_DIT"/> root. You may circumvent this obstacle
            by calling <command>dpkg-reconfigure</command>
            <option>slapd</option> after installation allowing to specify
            additional parameters manually.</para>
          </listitem>

          <listitem>
            <para>In case you **ever** loose your master <code>admin</code>
            password see <link
            xlink:href="http://techiezone.rottigni.net/2011/12/change-root-dn-password-on-openldap">Change
            Root DN Password on OpenLDAP</link> for troubleshooting.</para>
          </listitem>
        </orderedlist>
      </tip>

      <para>You may have to install the dialog package as a prerequisite to
      the <productname>openldap</productname> server package:</para>

      <screen>aptitude install dialog
aptitude install slapd</screen>

      <para>based on the <xref linkend="glo_DNS"/> domain
      <code>mi.hdm-stuttgart.de</code> the default <command>slapd</command>
      package installer configures a <xref linkend="glo_DIT"/> having
      <code>dc=mi,dc=hdm-stuttgart,dc=de</code> as root by default. Change
      this to <code>dc=betrayer,dc=com</code> by executing
      <command>dpkg-reconfigure</command> <option>slapd</option>.</para>
    </section>

    <section xml:id="ldapCompanyLdif">
      <title>Populating your <xref linkend="glo_DIT"/>.</title>

      <para>Our aim is to populate our <acronym>LDAP</acronym> Server by the
      following company structure of organisational units and persons:</para>

      <figure xml:id="sdiLdapBetrayerComTree">
        <title>An example <acronym>LDAP</acronym> Tree</title>

        <mediaobject>
          <imageobject>
            <imagedata fileref="Fig/ldaptree.svg"/>
          </imageobject>
        </mediaobject>
      </figure>

      <para>The <productname
      xlink:href="https://directory.apache.org/studio">Apache Directory
      Studio</productname> allows for conveniently accessing and modifying
      your server's <xref linkend="glo_LDAP"/> trees.</para>

      <tip>
        <para>You may want to adjust occurrences of
        <code>dc=betrayer;dc=com</code> by your configured <xref
        linkend="glo_DIT"/> root.</para>

        <orderedlist>
          <listitem>
            <para>You need your initial configuration <code>admin</code>
            password to perform a bind operation using the
            <code>cn=admin,dc=betrayer,dc=com</code> <xref
            linkend="glo_DN"/>.</para>
          </listitem>

          <listitem>
            <para>If you choose <quote>Use existing entry as template</quote>
            don't forget to purge your copy's <property>entryCsn</property>
            attribute belonging to your template data record being unique
            within your <xref linkend="glo_DIT"/>.</para>
          </listitem>

          <listitem>
            <para>Suitable <property>objectClass</property> and unique key
            attribute choices:</para>

            <glosslist>
              <glossentry>
                <glossterm>Organisational units
                <property>department</property>,
                <property>software</property>, <property>financial</property>,
                <property>devel</property>,
                <property>testing</property></glossterm>

                <glossdef>
                  <glosslist>
                    <glossentry>
                      <glossterm><property>objectClass</property>:</glossterm>

                      <glossdef>
                        <para><property>organizationalUnit</property></para>
                      </glossdef>
                    </glossentry>

                    <glossentry>
                      <glossterm><xref linkend="glo_RDN"/> unique
                      attribute:</glossterm>

                      <glossdef>
                        <para><property>ou</property></para>
                      </glossdef>
                    </glossentry>
                  </glosslist>
                </glossdef>
              </glossentry>

              <glossentry>
                <glossterm>Employees Jim Smith, Audrey Bean:</glossterm>

                <glossdef>
                  <glosslist>
                    <glossentry>
                      <glossterm><property>objectClass</property>:</glossterm>

                      <glossdef>
                        <para><property>inetOrgPerson</property></para>
                      </glossdef>
                    </glossentry>

                    <glossentry>
                      <glossterm><xref linkend="glo_RDN"/> unique
                      attribute:</glossterm>

                      <glossdef>
                        <para><property>uid</property></para>
                      </glossdef>
                    </glossentry>

                    <glossentry>
                      <glossterm>Other attributes:</glossterm>

                      <glossdef>
                        <para><property>sn</property>,
                        <property>cn</property>,
                        <property>givenName</property>,
                        m<property>ail</property></para>
                      </glossdef>
                    </glossentry>
                  </glosslist>
                </glossdef>
              </glossentry>
            </glosslist>
          </listitem>
        </orderedlist>
      </tip>

      <para>When you are finished an <productname
      xlink:href="https://directory.apache.org/studio">Apache Directory
      Studio</productname> export dump of your tree might look like:</para>

      <programlisting language="ldif">dn: dc=betrayer,dc=com
objectClass: organization
objectClass: dcObject
objectClass: top
dc: betrayer
o: betrayer.com

dn: cn=admin,dc=betrayer,dc=com
objectClass: organizationalRole
objectClass: simpleSecurityObject
cn: admin
userPassword:: e1NTSEF9cEhFK0VQT0cyZ3lSeU9nanZGcXNXT2I1ekdzR2w5Q0Q=
description: LDAP administrator

dn: ou=departments,dc=betrayer,dc=com
objectClass: top
objectClass: organizationalUnit
ou: departments

dn: ou=software,ou=departments,dc=betrayer,dc=com
objectClass: top
objectClass: organizationalUnit
ou: software

dn: ou=financial,ou=departments,dc=betrayer,dc=com
objectClass: top
objectClass: organizationalUnit
ou: financial

dn: ou=devel,ou=software,ou=departments,dc=betrayer,dc=com
objectClass: top
objectClass: organizationalUnit
ou: devel

dn: ou=testing,ou=software,ou=departments,dc=betrayer,dc=com
objectClass: top
objectClass: organizationalUnit
ou: testing

dn: uid=bean,ou=devel,ou=software,ou=departments,dc=betrayer,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: Jim Bean
sn: Bean
givenName: Jim
mail: bean@betrayer.com
uid: bean
userPassword:: e3NtZDV9YVhKL2JlVkF2TDRENk9pMFRLcDhjM3ovYTZQZzBXeHA=

dn: uid=smith,ou=financial,ou=software,ou=departments,dc=betrayer,dc=com
...</programlisting>
    </section>

    <section xml:id="sdiLdapTestBind">
      <title>Testing a bind operation as non - <code>admin</code> user</title>

      <para>Use <productname
      xlink:href="https://directory.apache.org/studio">Apache Directory
      Studio</productname> to supply a <property>userPassword</property> to
      e.g.
      <code>uid=bean,ou=devel,ou=software,ou=departments,dc=betrayer;dc=com</code>
      (still binding as <code>cn=admin,dc=betrayer,dc=com</code>).</para>

      <para>Then configure a second <productname
      xlink:href="https://directory.apache.org/studio">Apache Directory
      Studio</productname> connection profile binding as
      <code>uid=bean,ou=devel,ou=software,ou=departments,dc=betrayer,dc=com</code>.</para>

      <tip>
        <para>Beware: Some password hash types may not be supported. SMD5 is
        known to work.</para>
      </tip>
    </section>

    <section xml:id="ldapExtendPosixAccount">
      <title>Extending an existing entry</title>

      <para>This exercise sheds some insight on schema support. <xref
      linkend="glo_LDAP"/> supports building types similar to classes in
      <acronym>OO</acronym> languages by means of <code>objectClass</code>
      definitions. On contrary these types are not static but allow for run
      time modification during an <xref linkend="glo_LDAP"/> object's life
      span.</para>

      <para>The entry
      <code>uid=bean,ou=devel,ou=software,ou=departments,dc=betrayer;dc=com</code>
      may be extended by the <code>objectclass</code>
      <code>posixAccount</code>. Construct a <acronym>LDIF</acronym> file to
      add the attributes <code>uidNumber</code>, <code>gidNumber</code> and
      <code>homeDirectory</code> by a modify/add operation.</para>
    </section>

    <section xml:id="ldapFilter">
      <title>Filter based search</title>

      <para>Execute the following <acronym>LDAP</acronym> filter based
      searches:</para>

      <itemizedlist>
        <listitem>
          <para>All users with a <code>uid</code> attribute value starting
          with the letter <quote>b</quote>.</para>
        </listitem>

        <listitem>
          <para>All entries with either a defined <code>uid</code> attribute
          or a <code>ou</code> attribute starting with letter
          <quote>d</quote>.</para>
        </listitem>

        <listitem>
          <para>All users entries within the whole <xref linkend="glo_DIT"/>
          having a gidNumber value of 100.</para>
        </listitem>

        <listitem>
          <para>All user entries belonging to the billing department having a
          <code>uidNumber</code> value greater than 1023.</para>
        </listitem>

        <listitem>
          <para>All user entries within the whole <xref linkend="glo_DIT"/>
          having a <code>commonName</code> containing the substring
          <quote>ei</quote>.</para>
        </listitem>

        <listitem>
          <para>All user entries within the whole <xref linkend="glo_DIT"/>
          belonging to gidNumber == 100 or having a <code>uid</code> value
          starting with letter <quote>t</quote>.</para>
        </listitem>
      </itemizedlist>

      <tip>
        <para><xref linkend="glo_Soft_ApacheDirectoryStudio"/> allows both for
        <link
        xlink:href="https://nightlies.apache.org/directory/studio/2.0.0.v20210717-M17/userguide/ldap_browser/tools_filter_editor_dialog.html">filtering</link>
        and <link
        xlink:href="https://nightlies.apache.org/directory/studio/2.0.0.v20210717-M17/userguide/ldap_browser/gettingstarted_search.html">searching</link>
        providing nifty features like attribute name completion and syntax
        highlighting. You may define:</para>

        <itemizedlist>
          <listitem>
            <para>The <xref linkend="glo_DIT"/> entry root element (being
            identified by its <xref linkend="glo_DN"/>) to start your search
            from.</para>
          </listitem>

          <listitem>
            <para>The search scope being either of object, one level or
            subtree.</para>
          </listitem>

          <listitem>
            <para>Boolean expressions based on attribute values.</para>
          </listitem>
        </itemizedlist>

        <para><acronym linkend="glo_CLI">CLI</acronym> support is available as
        well <link
        xlink:href="http://tldp.org/HOWTO/LDAP-HOWTO/utilities.html">here</link>.</para>
      </tip>
    </section>

    <section xml:id="ldapTest">
      <title>Accessing <xref linkend="glo_LDAP"/> data by a mail
      client</title>

      <para>The directory studio allows to reread the directory tree. As a
      different approach configure your local mail client (e.g.
      <productname>thunderbird</productname>) using your
      <acronym>LDAP</acronym> server for email address lookup.</para>
    </section>

    <section xml:id="sdiLdapConfig">
      <title><xref linkend="glo_LDAP"/> configuration</title>

      <para>In contrast to many other server configurations OpenLdap supports
      parameter configuration within its own database backend. In other words:
      Some parameters are not being stored in configuration files.</para>

      <para>Actually OpenLdap still supports an alternate configuration file
      based approach which may be activated. Its use however is discouraged
      according to the documentation:</para>

      <note xlink:href="http://www.openldap.org/doc/admin24/slapdconf2.html">
        <para>The older style <filename>slapd.conf</filename>(5) file is still
        supported, but its use is deprecated and support for it will be
        withdrawn in a future OpenLDAP release.</para>
      </note>

      <para>The <xref linkend="glo_DIT"/> style configuration may be altered
      by <xref linkend="glo_LDAP"/> clients like <command
      xlink:href="http://www.openldap.org/software/man.cgi?query=ldapmodify&amp;apropos=0&amp;sektion=0&amp;manpath=OpenLDAP+2.4-Release&amp;format=html">ldapmodify</command>
      using <filename>.ldif</filename> files. An alternate way requires
      altering the server's configuration
      <filename>/etc/ldap/slapd.d/cn\=config/olcDatabase\=\{0\}config.ldif</filename>.
      We start by gathering required information. Note: The
      <command>ldapsearch</command> command is part of the
      <package>openldap-utils</package> package:</para>

      <screen>ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config

# {0}config, config
dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth manage by * break
<emphasis role="bold">olcRootDN: cn=admin,cn=config</emphasis>

# {1}mdb, config
dn: olcDatabase={1}mdb,cn=config
...
<emphasis role="bold">olcRootDN: cn=admin,dc=hdm-stuttgart,dc=de
olcRootPW: {SSHA}7M0gUyHOH7cfK1z9amqgK0uQcn84AuYw</emphasis>
...</screen>

      <para>The above lines appear near in the tail section. We see two
      databases <code>{0}</code> and <code>{1}</code> representing two
      different <xref linkend="glo_DIT"/>s namely <code>cn=config</code> (the
      configuration database) and <code>dc=hdm-stuttgart,dc=de</code>
      (containing our <quote>actual</quote> directory data).</para>

      <para>The configuration database does have a <code>olcRootDN:
      cn=admin,cn=config</code> entry but a corresponding
      <parameter>olcRootPW</parameter> attribute is yet missing. This limits
      configuration access to <code>localhost</code>.</para>
      <para>External access e.g. by <productname
      xlink:href="https://directory.apache.org/studio">Apache Directory
      Studio</productname> requires adding this credential attribute. This
      first-time (bootstrapping) configuration must be done locally by means
      of an <xref linkend="glo_LDIF"/> file adding a (possibly different)
      hashed password:</para>

      <screen>root@sdi8a:~# cat ~/add_olcRootPW.ldif
dn: olcDatabase={0}config,cn=config
add: olcRootPW
olcRootPW: {ssha}pHE+EPOG2gyRyOgjvFqsWOb5zGsGl9CD</screen>

      <para>Activating this configuration my be effected by using <command
      xlink:href="http://www.openldap.org/software/man.cgi?query=ldapmodify&amp;apropos=0&amp;sektion=0&amp;manpath=OpenLDAP+2.4-Release&amp;format=html">ldapmodify</command>:</para>

      <screen>root@sdi8a:~# ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f ~/add_olcRootPW.ldif
modifying entry "olcDatabase={0}config,cn=config"</screen>

      <para>This activation in turn enables a new account:</para>

      <glosslist>
        <glossentry>
          <glossterm>Bind DN:</glossterm>

          <glossdef>
            <para><option>cn=admin,cn=config</option></para>
          </glossdef>
        </glossentry>

        <glossentry>
          <glossterm>Bind password</glossterm>

          <glossdef>
            <para><option>*****</option></para>
          </glossdef>
        </glossentry>

        <glossentry>
          <glossterm>Base DN</glossterm>

          <glossdef>
            <para><option>cn=config</option></para>
          </glossdef>
        </glossentry>
      </glosslist>

      <para>When configuring a connection to access this configuration
      database you may have to untick Apache Directory Studio's <quote>Get
      base DNs from Root DSE</quote> box in the <quote>Browser Options</quote>
      tab. Then configure "cn=config" in the <quote>Base DN:</quote>
      manually.</para>

      <para>We may now dynamically alter ourserver configuration parameters
      remotely using e.g. <link
      xlink:href="https://directory.apache.org/studio">Apache directory
      studio</link>:</para>

      <mediaobject>
        <imageobject>
          <imagedata fileref="Fig/ldapConfig.png"/>
        </imageobject>
      </mediaobject>

      <para>A prominent configuration change candidate is our server's log
      level: Depending on your success during subsequent exercises you may
      want to adjust <parameter
      xlink:href="http://www.zytrax.com/books/ldap/ch6/#loglevel">olcLogLevel</parameter>
      in <code>cn=config</code> appropriately.</para>

      <caution>
        <para>Using <link
        xlink:href="https://serverfault.com/questions/324608/how-do-i-get-openldap-on-centos-6-to-write-anything-to-its-log-files#answer-499902">using
        olcLogFile</link> will not override OpenLdap using your host's syslog
        facility: The file will be created (provided write permission is being
        granted) but log messages will still be written to
        <filename>/var/log/syslog</filename>.</para>

        <para>Thus creating a separate <filename>ldap.log</filename> file
        requires <link
        xlink:href="http://www.tldp.org/HOWTO/LDAP-HOWTO/logs.html">configuring
        your system's syslog daemon appropriately</link>. Current systems
        allow for creating a file i.e.
        <filename>/etc/rsyslog.d/slapd.conf</filename> containing the desired
        log data redirection. Do not forget to restart your service.</para>
      </caution>
    </section>

    <section xml:id="sdiSectLdapOsSupport">
      <title><xref linkend="glo_LDAP"/> based user login</title>

      <caution xml:id="sdiSectLdapOsSupportPamCaution">
        <para>In this exercise you'll modify your system's <quote
        xlink:href="https://www.redhat.com/sysadmin/pluggable-authentication-modules-pam">Pluggable
        Authentication Modules (PAM)</quote>. You may
        <emphasis>easily</emphasis> get locked out due to an unintended
        misconfiguration. Stick to the following procedures avoiding this type
        of mishap:</para>

        <orderedlist>
          <listitem>
            <para>Create a backup <filename>/root/pam.tgz</filename> of your
            working <acronym>PAM</acronym> configuration being represented by
            <filename>/etc/pam.conf</filename> and files below
            <filename>/etc/pam.d</filename> beforehand:</para>

            <screen>cd /etc
tar zcf /root/pam.tgz pam.conf pam.d</screen>

            <para>Check the resulting archive to contain something
            like:</para>

            <screen language="bash">root@sdi12b:~# tar ztf /root/pam.tgz 
pam.conf
pam.d/
pam.d/newusers
pam.d/sshd
...
pam.d/chfn
pam.d/chsh</screen>
          </listitem>

          <listitem>
            <para>Always keep an independent (emergency) shell open when
            tinkering with <acronym>PAM</acronym>. In case you are no longer
            able to log in <abbrev>i.e.</abbrev> using <xref
            linkend="glo_ssh"/> this one allows for restoring your working
            configuration:</para>

            <screen language="bash">cd /etc
mv pam.d pam.d.orig        # Save your current (not working) PAM
mv pam.conf pam.conf.orig  # configuration for later inspection.

tar zxf /root/pam.tgz      # Restore your working PAM configuration</screen>

            <para>After this try to log in again.</para>
          </listitem>

          <listitem>
            <para>Prior to rebooting (and thus loosing your emergency login
            shell) always try logging in thereby testing your system's
            accessibility.</para>
          </listitem>
        </orderedlist>
      </caution>

      <para>Configure your second VM (the one without <xref
      linkend="glo_LDAP"/> Server) to allow for user login purely based on
      <xref linkend="glo_LDAP"/>.</para>

      <itemizedlist>
        <listitem>
          <para>Activation of OS level <xref linkend="glo_LDAP"/> user, group
          and password support is being outlined in <link
          xlink:href="https://computingforgeeks.com/how-to-configure-ubuntu-as-ldap-client">Configure
          LDAP Client on Ubuntu</link>.</para>
        </listitem>

        <listitem>
          <para><xref linkend="glo_LDAP"/> user entry DN's must be addressed
          by uid e.g. <code>uid=ldaptest,ou=people,...</code> . On successful
          configuration you should see:</para>

          <screen>$ id ldaptest
uid=1001(ldaptest) gid=1001(ldaptest) groups=1001(ldaptest)</screen>

          <para>A <quote>id: ‘<code>ldaptest</code>’: no such user</quote>
          message indicates your <xref linkend="glo_LDAP"/> setup does not
          (yet) work.</para>

          <tip>
            <itemizedlist>
              <listitem>
                <para><filename>/etc/nsswitch.conf</filename> should
                contain:</para>

                <programlisting language="none">passwd:         files ldap
group:          files ldap
shadow:         files ldap</programlisting>

                <para>What does this mean?</para>
              </listitem>

              <listitem>
                <para>Shut down you <command
                xlink:href="https://linux.die.net/man/8/nscd">nscd</command>
                daemon. Why?</para>
              </listitem>

              <listitem>
                <para>Your Secure Shell Daemon configuration
                /etc/ssh/sshd_config should contain:<programlisting
                language="bourne">PasswordAuthentication yes</programlisting></para>
              </listitem>

              <listitem>
                <para>After changing your configuration a reboot might be
                required.</para>
              </listitem>

              <listitem>
                <para>For debugging login attempts you may want setting your
                <xref linkend="glo_LDAP"/> server's <link
                xlink:href="http://www.zytrax.com/books/ldap/ch6/#loglevel">logging
                level</link> to at least including <option>conns</option>,
                <option>config</option> and <option>stats</option>.</para>
              </listitem>
            </itemizedlist>
          </tip>
        </listitem>
        <listitem>
          <para>Create the required user home directory manually beforehand
          setting owner and group accordingly.</para>
        </listitem>
      </itemizedlist>

      <tip>
        <para><xref linkend="glo_LDAP"/> user information
        (<property>uid</property>, common name, numerical id, group
        information ...) will reside on your <xref linkend="glo_LDAP"/> Server
        rather than locally in <filename>/etc/passwd</filename>,
        <filename>/etc/group</filename> and
        <filename>/etc/shadow</filename>.</para>
      </tip>
    </section>

    <section xml:id="diSectLdapBackupRestore">
      <title>Backup and recovery / restore</title>

      <para>Take the hard way to test backup and restore:</para>

      <orderedlist>
        <listitem>
          <para>Set up a <quote>replica</quote> <xref linkend="glo_LDAP"/>
          Server on your second host system.</para>
        </listitem>

        <listitem>
          <para>Export both databases (configuration and <quote>real</quote>
          data) from your production server using <command
          xlink:href="http://linux.die.net/man/8/slapcat">slapcat</command>.</para>
        </listitem>

        <listitem>
          <para>Restore the exported data on your replica using <command
          xlink:href="http://linux.die.net/man/8/slapadd">slapadd</command>.</para>
        </listitem>
      </orderedlist>
    </section>

    <section xml:id="sdiSectLdapByJava">
      <title>Accessing <xref linkend="glo_LDAP"/> by a <xref
      linkend="glo_Java"/> application.</title>

      <para>Accessing <xref linkend="glo_LDAP"/> requires a suitable client
      component. A standard <xref linkend="glo_JDK"/> or <xref
      linkend="glo_JRE"/> ships with a <acronym
      xlink:href="https://docs.oracle.com/javase/7/docs/technotes/guides/jndi/jndi-ldap.html">JNDI</acronym>
      provider. The <xref linkend="glo_API"/> however requires a lot of
      boilerplate code.</para>

      <para><orgname xlink:href="http://www.ldaptive.org">Ldaptive</orgname>
      offers a promising client provider <xref linkend="glo_API"/>. Start a
      <xref linkend="glo_Maven"/> based <xref linkend="glo_Soft_Eclipse"/>
      project which reads your own HdM <xref linkend="glo_LDAP"/> data being
      provided by the MI replica server
      <code>ldap1.mi.hdm-stuttgart.de</code>.</para>

      <para>This server allows for retrieving all attributes belonging to your
      personal records. Thus an authenticated bind using your HdM credentials
      is mandatory. Use <xref linkend="glo_TLS"/> to prevent password
      sniffing!</para>

      <tip>
        <itemizedlist>
          <listitem>
            <para>Read the <link
            xlink:href="http://www.ldaptive.org/#quick-start-guide">quick
            start guide</link> and consult the <link
            xlink:href="http://www.ldaptive.org/javadocs">Ldaptive
            API</link>.</para>
          </listitem>

          <listitem>
            <para>Using <orgname
            xlink:href="http://www.ldaptive.org">Ldaptive</orgname> may be
            accomplished by adding the following <xref linkend="glo_Maven"/>
            dependencies to your project's <filename>pom.xml</filename>
            file:</para>

            <programlisting language="none">&lt;project xmlns="http://maven.apache.org/POM/4.0.0" ... &gt;

  &lt;properties&gt;
    &lt;slf4j.version&gt;<emphasis role="red">find my current version on Maven central</emphasis>&lt;/slf4j.version&gt;
       ...
  &lt;/properties&gt;

  &lt;dependencies&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.ldaptive&lt;/groupId&gt;
      &lt;artifactId&gt;ldaptive&lt;/artifactId&gt;
      &lt;version&gt;<emphasis role="red">find my current version on Maven central</emphasis>&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt; &lt;!-- required for ldaptive's internal logging --&gt;
      &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
      &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
      &lt;version&gt;${slf4j.version}&lt;/version&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
      &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
      &lt;artifactId&gt;slf4j-simple&lt;/artifactId&gt;
      &lt;version&gt;${slf4j.version}&lt;/version&gt;
    &lt;/dependency&gt; ...</programlisting>

            <tip>
              <para>Follow <link
              xlink:href="https://www.swtestacademy.com/how-to-configure-simple-logger-slf4j-log-levels">How
              To Configure Simple Logger slf4j Log Levels</link> avoiding
              unrelated <productname>Ldaptive</productname> logging
              info.</para>
            </tip>
          </listitem>
        </itemizedlist>
      </tip>

      <para>The expected output with respect to the given initial data should
      resemble:</para>

      <screen>ou=departments,dc=betrayer,dc=com
  ou: {departments}
  objectClass: {top, organizationalUnit}
  -------------------------------------------------------
    ou=software,ou=departments,dc=betrayer,dc=com
      ou: {software}
      objectClass: {top, organizationalUnit}
      -------------------------------------------------------
        ou=devel,ou=software,ou=departments,dc=betrayer,dc=com
          ou: {devel}
          objectClass: {top, organizationalUnit}
          -------------------------------------------------------
            uid=bean,ou=devel,ou=software,ou=departments,dc=betrayer,dc=com
              uid: {bean}
              mail: {bean@betrayer.com}
              givenName: {Jim}
              cn: {Jim Bean}
              sn: {Bean}
              objectClass: {top, person, organizationalPerson, inetOrgPerson, posixAccount}
              userPassword: {{smd5}aXJ/beVAvL4D6Oi0TKp8c3z/a6Pg0Wxp}
              gidNumber: 1000
              homeDirectory: /home/bean
              uidNumber: 1000
              -------------------------------------------------------
        ou=testing,ou=software,ou=departments,dc=betrayer,dc=com
          ou: {testing}
          objectClass: {top, organizationalUnit}
          -------------------------------------------------------
    ou=financial,ou=departments,dc=betrayer,dc=com
      ou: {financial}
      objectClass: {top, organizationalUnit}

...</screen>

      <para>Remarks:</para>

      <itemizedlist>
        <listitem>
          <para>Descend a given arbitrary LDAP tree recursively.</para>
        </listitem>

        <listitem>
          <para>Indent according to each entries hierarchy level. In the above
          example <code>ou=software,ou=departments,dc=betrayer,dc=com</code>
          being a child of <code>ou=departments,dc=betrayer,dc=com</code>
          receives an extra indent.</para>
        </listitem>

        <listitem>
          <para>Mind single and multi valuedness of attributes: In the above
          example <code>mail: {bean@betrayer.com}</code> is multivalued in
          contrast to <code>homeDirectory: /home/bean</code>. The brace pairs
          {...} denote attribute sets. The server's schema information is your
          friend. Consider the following hints:</para>

          <programlisting language="java">ConnectionFactory factory = DefaultConnectionFactory ... ;
Schema schema = SchemaFactory.createSchema(factory);

... schema.getAttributeType(...).isSingleValued() ...</programlisting>
        </listitem>
      </itemizedlist>
    </section>
  </section>
</chapter>