-
Goik Martin authoredGoik Martin authored
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 < 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&apropos=0&sektion=0&manpath=OpenLDAP+2.4-Release&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&apropos=0&sektion=0&manpath=OpenLDAP+2.4-Release&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"><project xmlns="http://maven.apache.org/POM/4.0.0" ... >
<properties>
<slf4j.version><emphasis role="red">find my current version on Maven central</emphasis></slf4j.version>
...
</properties>
<dependencies>
<dependency>
<groupId>org.ldaptive</groupId>
<artifactId>ldaptive</artifactId>
<version><emphasis role="red">find my current version on Maven central</emphasis></version>
</dependency>
<dependency> <!-- required for ldaptive's internal logging -->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency> ...</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>