\documentclass[a4paper,english]{article}
\usepackage[T1]{fontenc}
\usepackage[latin1]{inputenc}
%\pagestyle{empty}
\usepackage{babel}
\usepackage{graphics}
%\date{02 March 2003}

\makeatletter

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LyX specific LaTeX commands.
\providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\@}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Textclass specific LaTeX commands.
 \newenvironment{lyxcode}
   {\begin{list}{}{
     \setlength{\rightmargin}{\leftmargin}
     \raggedright
     \setlength{\itemsep}{0pt}
     \setlength{\parsep}{0pt}
     \normalfont\ttfamily}%
    \item[]}
   {\end{list}}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% User specified LaTeX commands.
\usepackage[breaklinks]{hyperref}
%\pagestyle{empty}
\usepackage{times}

\makeatother
\begin{document}

\title{Using LDAP from Java+Tomcat and Python+Zope}


\author{Holger Blasum, Andreas Meisl}

\maketitle
(Talk at the \href{http://www.guug.de/veranstaltungen/ffg2003/}{GUUG-Frühjahrsfachgespräch}
Bochum, 28 March 2003.)

\begin{abstract}
We report on the implementation and installation and implementation of
clients to Novell eDirectory via plain text and TLS/SSL and some performance measurements. Note:
You can find source code and supplementary materials at

\href {http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/}{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/}.
\end{abstract}

\section{Use case }

%\thispagestyle{empty}

The Dept of Veterinary Science at Munich University is using Novell
eDirectory as directory server for student user data%
\footnote{This setup is quite popular in Bavarian universities, see AK Netz
PC, \href{http://www-lan.uni-regensburg.de/ak/netz/}{http://www-lan.uni-regensburg.de/ak/netz/}.
}. Once per term, students can subscribe to elective courses; since
July 2002 this is done via a web interface%
\footnote{\href{http://www.vetmed.uni-muenchen.de/studium/wahlpflicht/kurse/index.html}{http://www.vetmed.uni-muenchen.de/studium/wahlpflicht/kurse/index.html}
}. 


\section{Directory services and LDAP}

Examples for directories are telephone registers or the DNS system;
directories adhere to the hierarchical data model. In 1988, CCITT(ITU)
/ ISO finalized the standard X.500 on directory services \cite{Kla01,Chad96}.
LDAP%
\footnote{RFC 1777 (Version 2), 2251 (Version 3)
} is a trimmed down variety of the directory accesss protocol (DAP) for
X.500 directory services. Originally designed for the replication
of directory data, LDAP is now provided as an interface by popular
directory services, e.g OpenLDAP%
\footnote{\href{http://www.openldap.org/}{http://www.openldap.org/}
}, Novell eDirectory%
\footnote{previously: Novell Directory Services (NDS), \href{http://www.novell.com/products/edirectory/}{http://www.novell.com/products/edirectory/}
} , or Microsoft Active Directory%
\footnote{\href{http://www.microsoft.com/windows2000/technologies/directory/ad/default.asp}{http://www.microsoft.com/windows2000/technologies/directory/ad/default.asp}
}.

In these applications, LDAP is typically used via TCP (although there
is also a specification for connectionless UDP%
\footnote{RFC 1798
}).

\vspace{0.3cm}
{\resizebox*{1\textwidth}{!}{\includegraphics{image/ldapethereal.eps} \par}
\vspace{0.3cm}

LDAP makes use of ASN.1/BER encoding. A tcpdump (\emph{tcpdump -s
0 -w filename}) of an LDAP session can be inspected with a packet
analyzer such as Ethereal%
\footnote{\href{http://www.ethereal.com/}{http://www.ethereal.com/}
} that also understands ASN.1/BER and LDAP application classes. On
the screeshot you see a \emph{modify} request of LDAP version 3.


\section{A sand box}

Both OpenLDAP as well as eDirectory a freely available (free as speech
in the former, free as beer in latter case). As we just want to test
clients, the most simple thing to do is to use the test site
provided by Novell%
\footnote{\href{http://www.nldap.com/}{http://www.nldap.com/}, in \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/conf/nldapcom/nldapaccount.txt}{nldapaccount.txt}
you will find complete access data (\emph{cn/userPassword}) for the
current account. If you like to set up an account yourself, just follow
the links \emph{eDirectory Account Adminstration->create NDS Container
\& its admin user}.
}.

\vspace{0.3cm}
{\resizebox*{1\textwidth}{!}{\includegraphics{image/repview.eps}} \par}
\vspace{0.3cm}

Once you enter this sandbox via a GUI such as the \emph{LDAP browser/editor}%
\footnote{\href{http://www.iit.edu/~gawojar/ldap/}{http://www.iit.edu/~gawojar/ldap/}
}, or Novell's native tools (nwadmin, Console1), you can easily grasp
the simple structure of the sandbox. There a zoology courses that
can be registered by students (Register/Unregister). Alternatively,
via the command line, one can easily generate a an \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/nldapcom/repositoryldif.txt}{LDIF file} (that
is also human readable): 

\begin{lyxcode}
ldapsearch~-vLx~-b~\char`\"{}ou=subldap,~ou=user,

o=novell\char`\"{}~-h~www.nldap.com

\char`\"{}(objectclass={*})\char`\"{}~>~subldap.ldif
\end{lyxcode}

\section{Simple LDAP clients from the command line interface}


\subsection{Installation}

Java: We have used the Blackdown port%
\footnote{\href{http://www.blackdown.org/}{http://www.blackdown.org/}
}, preferably Java 1.4.1%
\footnote{For Java 1.3 one has to supplement the Java Secure Socket Extension
(JSSE) 1.0.2 into the Java extension directory; JSSE is already included
in Java 1.4.~
}. The next step is to get a copy of the Java Novell Developer Kit%
\footnote{\href{http://developer.novell.com/ndk/jldapunx.htm}{http://developer.novell.com/ndk/jldapunx.htm},
15 November 2002, also linked from \href{http://www.openldap.org/jldap/}{http://www.openldap.org/jldap/}.
Documentation and examples also can be found in that package; extensive
printed documentation is also available in \cite{Har00}). 
}, but the only file we really need is \emph{ldap.jar}, that we install
to \emph{/usr/lib/j2sdk1.4.1/jre/lib/ext/} (or adapt the class path
alternatively).

Python: We use Python 2.1.3 and the python-ldap library%
\footnote{\href{http://python-ldap.sourceforge.net/}{http://python-ldap.sourceforge.net/}
} as of release 2.0.0pre06 (that one goes to \emph{/usr/lib/python2.1/site-packages}).

Server; OS: Additionally to the sandbox that we have already described,
we used an eDirectory 8.6.2 auf Netware 5.1%
\footnote{eDirectory also runs on AIX, Linux, Solaris, Windows.
} as directory server, python and java middleware had been installed
on a Debian Woody GNU/Linux 2.4.19 i686. 


\subsection{View course list (anonymously)}

We want to write a client that anonymously views the content of the
course directory, so we have the following tasks:

\begin{itemize}
\item initialize LDAP connection
\item send LDAP search request to server
\item print the list of results \emph{res}
\item close connection
\end{itemize}
Let's begin doing this in python (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/ListCourses.py}{ListCourses.py}):

\begin{lyxcode}
\#!/usr/bin/python~

import~ldap~

l=ldap.initialize(\char`\"{}ldap://www.nldap.com:389\char`\"{})

res~=~l.search\_s(\char`\"{}ou=200201,~ou=courses,

~~ou=zoology,~ou=subldap,~ou=user,

~~o=novell\char`\"{},~ldap.SCOPE\_ONELEVEL,~

~~\char`\"{}objectclass={*}\char`\"{})~

for~r~in~res:~

~~print~r{[}0{]}

c.unbind\_s()
\end{lyxcode}
Giving us (obviously):

\begin{lyxcode}
blasum@promitheas:\$~./ListCourses.py

cn=Ant,ou=courses,ou=zoology,ou=user,o=NOVELL

cn=Bee,ou=courses,ou=zoology,ou=user,o=NOVELL

cn=Cow,ou=courses,ou=zoology,ou=user,o=NOVELL


\end{lyxcode}
Note: {}``\_s'' used in the commands chooses the synchronous connection mode (client waits
until data has arrived). In the JLDAP API the choice of synchronous/asynchronous
is simply done by method overloading. The java client that here, as
well as in all other examples, also acts synchronously, looks like
this (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/ListCourses.java}{ListCourses.java}):

\begin{lyxcode}
import~java.io.{*};

import~com.novell.ldap.{*};

public~class~ListCourses~\{~

~~public~static~void~main~(String~{[}{]}~args)

~~~~throws~LDAPException~\{

~~~~LDAPConnection~c=new~LDAPConnection~();

~~~~c.connect~(\char`\"{}www.nldap.com\char`\"{},~389);~

~~~~LDAPSearchResults~res~=~c.search(~

~~~~~~\char`\"{}ou=200201,~ou=courses,~ou=zoology,

~~~~~~ou=subldap,~ou=user,~o=novell\char`\"{},~

~~~~~~LDAPConnection.SCOPE\_ONE,~null,~null,

~~~~~~false);

~~~~while~(res.hasMore())~\{

~~~~System.out.println(res.next().getDN());

~~~~c.disconnect();

~~~~\}

~~\}

\}


\end{lyxcode}
Disgression: If we eager to write python-like, but to have it interpreted
by Java, one can use the jython interpreter%
\footnote{\href{http://www.jython.org/}{http://www.jython.org/}
}. \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/ListCourses.jy}{ListCourses.jy}
then (a nice mixture of Python and LDAP) has this flavor:

\begin{lyxcode}
from~com.novell.ldap~import~{*}

c~=~LDAPConnection~()

c.connect~(\char`\"{}www.nldap.com\char`\"{},~389)

res~=~c.search(\char`\"{}ou=200201,~ou=courses,

~~ou=zoology,~ou=subldap,~ou=user,

~~o=novell\char`\"{},~~~~~~~~

~~LDAPConnection.SCOPE\_ONE,~None,~None,~0)~

while~res.hasMore():

~~~print~res.next().getDN()

c.disconnect()


\end{lyxcode}
Observation: Running java took 1.0 seconds, its python counterpart
consumed 0.14 seconds; jython got the job done in 4.5 seconds (+ ca.
0.5-1 seconds response time for the transcontinental LDAP query).
The interfaces of JLDAP and python-ldap look very similar, with python-ldap
being somewhat leaner.


\subsection{Change subscription data (as administrator)}

We want to use an authenticated connection to modify entries in the
directory. This boils down to the following steps:

\begin{itemize}
\item initialize LDAP connection
\item authenticate to server
\item send \emph{modify} request (consisting of a list of modifications)
\item close connection
\end{itemize}
Here it is in python (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Register.py}{Register.py}): 

\begin{lyxcode}
import~ldap

import~sys

if~len~(sys.argv)~!=~3:~

~~print~\char`\"{}Usage:~Register.py~course~student\char`\"{}

~~sys.exit(1)

ldap.set\_option~(ldap.OPT\_PROTOCOL\_VERSION,~ldap.VERSION3)

c=ldap.initialize(\char`\"{}ldap://www.nldap.com:389\char`\"{})

c.simple\_bind\_s(\char`\"{}cn=admin,~ou=subldap,

~~ou=user,~o=novell\char`\"{},~\char`\"{}secret\char`\"{})~

c.modify\_s~(\char`\"{}cn=\char`\"{}~+~sys.argv{[}1{]}~+~\char`\"{},~\char`\"{}~+~

~~\char`\"{}ou=200201,~ou=courses,~ou=zoology,

~~~ou=subldap,~ou=user,~o=novell\char`\"{},

~~{[}(ldap.MOD\_ADD,~\char`\"{}member\char`\"{},~\char`\"{}cn=\char`\"{}~+

~~sys.argv{[}2{]}~+~\char`\"{},\char`\"{}~+~\char`\"{}ou=students,

~~ou=zoology,~ou=subldap,~ou=user,

~~o=novell\char`\"{}){]})

c.unbind\_s()
\end{lyxcode}
The inverse operation looks like \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Unregister.py}{Unregister.py},
except for that here we replaced \emph{MOD\_ADD} by \emph{MOD\_DEL}.
The java versions \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Register.java}{Register.java}
and \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Unregister.java}{Unregister.java}
also just differ in \emph{ADD} vs \emph{DELETE}.

Observations: time consumption is 1.2 seconds for JLDAP, 0.11 seconds
for python-ldap. When using eDirectory one needs to set the
LDAP protocol version explicitly to 3 (otherwise \emph{simple\_bind\_s} silently
fails in python-ldap, whereas JLDAP has set vesion 3 as its default).
When \emph{simple\_bind\_s} succeeds, but the subsequent \emph{modify}
request fails, we get an exception (to keep it simple, we do not handle
it here). 


\subsection{Squeezing in an abstraction layer: \emph{ChangeManager}}

We don't really want to hardwire library specifics into our application,
thus we have an written an abstraction layer (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/ChangeManager.java}{ChangeManager.java}
/ \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/ChangeManager.py}{ChangeManager.py})
that wraps around LDAP commands (below the presentation logic layer):

\begin{lyxcode}
Python~2.1.3~(\#1,~Sep~12~2002,~00:24:18)

{[}GCC~2.95.4~20011002~(Debian~prerelease){]}~on~linux2~

>\,{}>\,{}>~import~ChangeManager~

>\,{}>\,{}>~c~=~ChangeManager.ChangeManager()~

>\,{}>\,{}>~courses~=~c.getCourseList('03')
\end{lyxcode}
By hiding the entire LDAP interaction into the ChangeManager one should
be able to e.g. switch to an entirely different (e.g. relational)
data repository.

The entire architecture is thus three-tiered: LDAP/eDirectory as repository,
an intermediate layer (ChangeManager) for logic and zope DTML or java
servlets for the presentation layer. If you are so inclined, you
could roughly map this to the Publisher/Subscriber pattern (e.g. \cite{Gam94})
with subscribers/observers embodied in command line or web interface
interaction and the publisher residing in the directory.


\section{Presentation layer}


\subsection{Zope DTML}

Naive approach: Using zope 2.5.1 (running on python 2.1) we (quite brutally) copied
(well, symlinked) our servlet \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/Courses.py}{Courses.py}
to \emph{/usr/lib/zope/Extensions}, and dumpled the infrastructure
(all other python classes) to \emph{/usr/lib/python2.1/.} The zope
instance \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/zope/wahl.xml}{Zope-Instanz}
now houses a DTML page (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/zope/index.html.txt}{index.html})
that via 

\begin{lyxcode}
<dtml-var~expr=\char`\"{}doGet(~term=REQUEST{[}'term'{]},

course=REQUEST{[}'course'{]},~student=REQUEST{[}'student'{]},

password=REQUEST{[}'password'{]},

raction=REQUEST{[}'raction'{]})\char`\"{}>


\end{lyxcode}
calls a zope \emph{External Method} (\emph{doGet}), that represents
a reference to the method \emph{doGet} in \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/Courses.py}{Courses.py}.
If we want changes in Courses.py to take effect, we have to restart zope.

Integration as Zope component (''product''): In the product \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/Products/Courses}{Courses} we leave the LDAP connection open after usage and reopen it only if it does not persist any longer (self.c == None). We will see that this technique (learned from the LDAPUserFolder zope product) will save TLS/SSL handshakes.


\subsection{Tomcat Servlets}

The most popular provider of java CGI functionality for the web are
servlets or JSPs. We use tomcat 4.1.18 for this. During installation
and development it is easy to make tomcat debug friendly (via debug
levels and reloadable in \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/tomcat/server.xml}{server.xml}),
one should reset this in the production environment. Depending on
the java security mode defaults, it may be necessary to tweak \emph{SocketPermissions}
in \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/tomcat/04webapps.policy}{policy files}
the default (that was necessary in the debian tomcat package, but
not necessary when directly downloading from jakarta.apache.org).
\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/util/src/Courses.java}{Courses.java}
is the complete servlet.


\section{TLS/SSL }

Having done the proof of concept it is wise to secure the whole thing,
this entails both the communication between web server and browser
on the one hand, as well as the communication between web server and
LDAP resitory (eDirectory server). 

SSL has originally been developed and specified by Netscape, this
was rebaptized TLS when further standard development moved to an eponymous
IETF working group, due to their similarity both protocols are often
mentioned together \cite{Re01}. In current practice, during the handshake,
most clients offer the server a dozen or more transmission options,
whereof the server picks one to its taste. The growing popularity
if TLS is demonstrated by the fact that TLS now is preference of both
OpenLDAP \emph{slapd} 2.1.12 as well as eDirectory 8.7 (whereas eDirectory
8.6.2 still goes for SSL).

The assigned port for LDAP over TLS/SSL is 636. First we establish
a TLS/SSL connection; once the handshake has succeeded, we can use
TLS/SSL \emph{data} transmissions to run the LDAP protocol. It is
just the same idea as using HTTPS protokoll (on port 443)%
\footnote{Slightly misleading to the aspiring TLS/SSL LDAP adept is the fact
that there is also an RFC 2830 for TLS initialization \emph{via already
initialized} unencrypted LDAP sessions, that RFC is however rarely
implemented. 
}.


\subsection{Web server $\leftrightarrow$ Browser}

Although tomcat and zope both offer SSL support, we chose to hide
both behind \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/conf/apache/vhost.conf}{apache's SSL module}
(\emph{lib\_modapache\_ssl}), one way to do this is the \emph{ProxyPass}
instruction (rationale: one single point for web server certificates,
the apache SSL module has been widely used).


\subsection{Web server $\leftrightarrow$ LDAP server}


\subsubsection*{Background}

To rule out man-in-the-middle attacks, the X.509 certificate of the
\emph{certification agency that had once certified the LDAP server's
public key} (CA certificate; this is exactly the certificate of the
CA that has signed the LDAP server certificate being transmitted in
the second step of the handshake) has to be transferred to the client
manually once \cite{Mar02}, when doing this mind that:

\begin{itemize}
\item In the LDAP server certificate for the \emph{Subject name (cn)} use
the FQDN (fully qualified domain name) of the LDAP server (same idea
as with the certificates used for HTTPS, apparently this is not default
neither in Novell nor the \emph{openssl} tools).
\item The CA certificate (e.g. \emph{Institutional CA} in an eDirectory)
must be exported to a file (Novell's \emph{b64} format is the same as \emph{PEM}
in OpenLDAP tools), it then can be presented to the client in a suitable
format. 
\end{itemize}
For inspecting TLS/SSL transactions ({}``did we use encryption or
not; what protocol are we actually using ?'') the most useful tools were:
\emph{ssldump}%
\footnote{\href{http://www.rtfm.com/ssldump/}{http://www.rtfm.com/ssldump/}
} (you see all TLS/SSL transactions captured by on a network interface),
\emph{ldapsearch -d 7}%
\footnote{\href{http://www.openldap.org/}{http://www.openldap.org/}
} (verbose, once debug level is high enough, same applies for slapd).
X.509 certificates can either be watched in the output of these debuggers
or statically using {}``\emph{openssl x509 -noout -text -in cert.pem}''.


\subsubsection*{Python client}

Since version 2.0.0pre06 python-ldap also runs over TLS/SSL (in the
handshake the openssl negiotiated \emph{RSA with 3DES EDE CBC SHA}
with a default eDirectory 8.6/8.7. Using 

\begin{lyxcode}
ldap.set\_option~(ldap.OPT\_X\_TLS\_CACERTFILE,

~~\char`\"{}\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/nldapcacert.pem}{nldapcacert.pem}\char`\"{})
\end{lyxcode}
we tell the client, where to find the CA certificate.

Then we just have to adapt the LDAP URL, i.e. (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/RegisterSSL.py}{RegisterSSL.py}): 

\begin{lyxcode}
c~=~ldap.initialize~(\char`\"{}ldaps://www.nldap.com:636\char`\"{})~
\end{lyxcode}
instead of previously ( \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/Register.py}{Register.py}):

\begin{lyxcode}
c~=~ldap.initialize~(\char`\"{}ldap://www.nldap.com:389\char`\"{}).
\end{lyxcode}
To make the whole thing really work, otherwise we see:

\begin{lyxcode}
ldap.SERVER\_DOWN:~\{'desc':~\char`\"{}Can't~contact~LDAP~server\char`\"{}\}
\end{lyxcode}
make sure that: 

\begin{itemize}
\item The openldap libraries installed on the client have to support TLS/SSL
(e.g. for a debian \emph{stable} supplement \emph{libldap2-tls} from
\emph{testing}).
\end{itemize}

\subsubsection*{Java client}

Using the \emph{keytool} in a we typically store the the CA certificate
in a \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/nldapcacert.keystore}{file} in the keystore format, that is then read in \href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/standalone/RegisterSSL.java}{RegisterSSL.java}
by a call to \emph{java.security.Security. addProvider()}%
\footnote{Following the hints by Susan Perrin 04 June 2002 on novell.devsupp.ldap\_j,
04 June 2002.
}: Looking at the protocol's handshake a default JSSE seems to prefer
\emph{RSA with RC4 MD5} (optimized for speed).


\section{Discussion}


\subsection{Accessibility of eDirectory via LDAP}

The LDAP interface of eDirectory \cite{Ser02} seems to be mostly
conformant to the LDAPv3 User Schema (RFC 2256); a (meaningful) deviation
was that the \emph{userPassword} attribute could be evaluated only
via \emph{compare} operations, but not with read operations%
\footnote{\href{ http://groups.google.com/groups?threadm=r4Xy7.2399\%241v1.8347\%40prv-forum2.provo.novell.com}{Andy Chalarambous 16 Oct 2001 on novell.support.ds.nds-general}
}, otherwise we did not encounter any deviations. 

A challenge in using eDirectory via the LDAP interface as compared
to Novell's more native interfaces (DirXML, NJCL java libraries) is
given in that LDAP as a lightweight protocol does not support transactions,
whereas eDirectory sometimes usess non-atomic operations (e.g. group
membership in eDirectory is both an attribute of the group as well
as an attribute of the user). Our response was to define group membership
(=course subscription) as a group attribute only, this can be done
without problems. 

When we used group membership we also ran into one instance where
the mapping of the eDirectory was not yet stable: in version 8.6.1
group membership was the attribute \emph{member}, in version 8.6.2
it was \emph{uniqueMember} (both are RFC 2256 compatible).


\subsection{Performance and Usability}

Simulating 400 concurrent users for 60 seconds under \emph{siege}%
\footnote{\href{http://www.joedog.org/siege/index.shtml}{http://www.joedog.org/siege/index.shtml}
} we obtained the following performance (399MHz PII, 796 bogomips,
128 MB RAM):

\begin{tabular}{|l|r|}
\hline 
\selectlanguage{english}
Apache directly
\selectlanguage{english}&
\selectlanguage{english}
25-35 transactions/sec.
\selectlanguage{english}\\
\hline
\hline 
\selectlanguage{english}
Apache+Java+Tomcat (w/o TLS/SSL)
\selectlanguage{english}&
\selectlanguage{english}
10-12 transactions/sec.
\selectlanguage{english}\\
\hline 
\selectlanguage{english}
Apache+Zope+Python (w/o TLS/SSL)
\selectlanguage{english}&
\selectlanguage{english}
10-12 transactions/sec.
\selectlanguage{english}\\
\hline 
\selectlanguage{english}
Apache+Java+Tomcat (with TLS/SSL, no pooling)
\selectlanguage{english}&
\selectlanguage{english}
3.2 transactions/sec.
\selectlanguage{english}\\
\hline 
\selectlanguage{english}
Apache+Zope+Python (with TLS/SSL, no pooling)
\selectlanguage{english}&
\selectlanguage{english}
3.2 transactions/sec.
\selectlanguage{english}\\
\hline
\selectlanguage{english}
Apache+Zope+Python (with TLS/SSL, pooling)
\selectlanguage{english}&
\selectlanguage{english}
10-12 transactions/sec.
\selectlanguage{english}\\
\hline
\end{tabular}

The time differential we measured due to the initial loading of the
java virtual machine flattens out when one uses an up-to-date version
of tomcat (such as 4.1.18, whereas the java+tomcat combination had
performed very poorly when using the less recent tomcat version 4.0.6,
where we had a performance worse by facter of 2 to 8). 

Obviously, performance of \emph{with TLS/SSL} is improved (an
approximation of the value \emph{with TLS/SSL} to those \emph{without
TLS/SSL} is to be expected) if, instead of opening a new connection
for each TLS/SSL transaction between web server and LDAP server we
just reuse a connection that has been opened once (this kind of pooling 
was achieved by choosing the implementation as a zope product leaving 
connections open).

Taking into account these uniform performance measurements, our juxtaposition
of library interfaces (\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/src/doc/api.txt}{api.txt}),
may both be helpful to persons having done their prototype in java
and now wanting to do it python (as we did) or for those migrating
into the converse direction. The similarity of the ldap libraries
is probably due to the explict specification of a C API in RFC 1823;
when using the JLDAP API the price we had to pay for more fine-grained
typing (e.g. searches on attributes return \emph{com.novell.ldap.AttributeSet}
instances in JLDAP, whereas python-ldap just gives us hashes of lists)
were more type conversions in our ChangeManager.


\subsection{TLS/SSL for python and zope}

In a previous article with a similar point of view as ours \cite{Ri01a}
then non-availability of LDAPv3 and this LDAP over TLS/SSL was still
a problem for python programmers. This has been solved, since Michael
Ströder introduced LDAPv3 support into in die python-ldap-2.0.0pre06
in summer 2002. As most zope applications for LDAP (such as \emph{LDAPUserFolder},
\emph{CMFUserFolder}, \emph{LDAPDirectoryManager}, \emph{LDAPAdapter},
\emph{ZopeLDAP}) use python-ldap (ldaptor would be an alternative),
the use of TLS/SSL is possible, once the user updates the openldap
and python-ldap library (see \cite{Chou03}) and provides all the
CA certificates needed for the connection.


\subsection{A tribute to {}``Directories vs databases''}

Concerning the question {}``Directory or database?'' our use case
is on the borderline: because course selections fit well into existing
LDAP infrastructure (a simple addition of groups and membership attributes
in an existirng directory, e.g. no changes to the schema were made)
and only lightweight data has been stored the use of a directory can
be justified (similar to \cite{Ri01b}); this not necessarily needs
to hold for more complicated and rather relational use cases \cite{Koehn02,Ma01}.
The decision to implement this use case via a directory was also motivated
to gain experience in the accessibility of LDAP repositories which
may be useful for more typical tasks (authentication, certificate
management etc).


\section{Acknowledgments}

The use case is due to Prof Klaus Osterkorn; thanks also go to Christoph
Wunder and Helmut Naughton for some{}``SSL sessions''.


\section{Sources, Addenda und Corrigenda}

\href{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/talkback/}{http://www.blasum.net/holger/wri/comp/net/7appl/ldap/bochum2003/talkback/}.

\begin{thebibliography}{Koehn02}
\begin{raggedright}
\bibitem[Chad96]{Chad96}David Chadwick, Understanding X.500 - The Directory, \href{http://www.isi.salford.ac.uk/staff/dwc/Version.Web/Contents.htm}{http://www.isi.salford.ac.uk/staff/dwc/Version.Web/Contents.htm}.
\bibitem[Chou03]{Chou03}TC Chou, Setting LDAP, 2003, \href{http://zope.slat.org/Members/~tcchou/~index_html/setting_ldap}{http://zope.slat.org/Members/tcchou/index\_html/setting\_ldap}.
\bibitem[Gam94]{Gam94}Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Design Patterns,
Addison-Wesley 1994.
\bibitem[Har00]{Har00}Robert G. Harrison, Jim Sermersheim, and Steve Trottier, LDAP Developer's
Guide, Novell Press 2000.
\bibitem[Kla01]{Kla01}Norbert Klasen, Directory Services for Linux in comparison with Novell
NDS and Microsoft Active Directory, Diploma Thesis, RWTH Aachen 2001,
\href{http://www.daasi.de/staff/norbert/thesis/html/}{http://www.daasi.de/staff/norbert/thesis/html/}
.
\bibitem[Koehn02]{Koehn02}Kristian Köhntopp, Directory Services vs. Relationale Datenbanken,
2002, \href{http://www.koehntopp.de/kris/artikel/dir-vs-rel/}{http://www.koehntopp.de/kris/artikel/dir-vs-rel/}.
\bibitem[Ma01]{Ma01}Vikas Mahajan, Directory, Database, or Both?, \href{http://www.ldapzone.com/~general\_interest.html\#2}{http://www.ldapzone.com/general\_interest.html\#2}.
\bibitem[Mar02]{Mar02}Franck Martin, SSL Certificates HOWTO, \href{http://www.ibiblio.org/~pub/Linux/docs/~HOWTO/~other-formats/html\_single/~SSL-Certificates-HOWTO.html}{http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/html\_single/SSL-Certificates-HOWTO.html}.
\bibitem[Ra]{Ra}Raphinou, ldap installation with ssl, \href{http://www.raphinou.com/~ldaps/~LDAP-SSL.HOWTO}{http://www.raphinou.com/ldaps/LDAP-SSL.HOWTO}.
\bibitem[Re01]{Re01}Erich Rescorla, SSL and TLS, Addison-Wesley 2001.
\bibitem[Ri01a]{Ri01a}Mike Richichi, How to Use Perl, Python, and PHP to Access NDS eDirectory
8.5 via LDAP, Novell Appnotes, \href{http://developer.novell.com/research/appnotes/2001/august/05/~a010805.pdf}{http://developer.novell.com/research/appnotes/2001/august/05/a010805.pdf},
\href{ http://developer.novell.com/research/~appnotes/~2001/~august/05/apv.htm}{http://developer.novell.com/research/appnotes/2001/august/05/apv.htm}.
\bibitem[Ri01b]{Ri01b}Mike Richichi, ATTIC: a case study of directory-enabled course management,
Proceedings of 29th ACM SIGUCCS Conf, 2001, pp. 258 - 261.
\bibitem[Ser02]{Ser02}Jim Sermersheim, LDAP Schema for NDS, 2002, \href{http://www.ietf.org/internet-drafts/draft-sermersheim-nds-ldap-schema-03.txt}{http://www.ietf.org/internet-drafts/draft-sermersheim-nds-ldap-schema-03.txt}.
\end{raggedright}
\end{thebibliography}

\end{document}
