From d20452c6fe3b31658aec41ae3bb6ce53dc9f0f1b Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Fri, 20 Apr 2012 12:23:07 +0000
Subject: [PATCH] More advice for LDAP application developers

---
 opendj3/src/main/docbkx/dev-guide/chap-best-practices.xml |  228 +++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 190 insertions(+), 38 deletions(-)

diff --git a/opendj3/src/main/docbkx/dev-guide/chap-best-practices.xml b/opendj3/src/main/docbkx/dev-guide/chap-best-practices.xml
index 441784c..386b729 100644
--- a/opendj3/src/main/docbkx/dev-guide/chap-best-practices.xml
+++ b/opendj3/src/main/docbkx/dev-guide/chap-best-practices.xml
@@ -59,7 +59,7 @@
   manage certificates rather than passwords, directory servers like OpenDJ can
   do client authentication as well.</para>
  </section>
- 
+
  <section xml:id="reuse-connections">
   <title>Reuse Connections</title>
 
@@ -111,70 +111,222 @@
   specific filters. As a rule, prefer equality filters over substring
   filters.</para>
 
+  <para>Some directory servers like OpenDJ reject unindexed searches by
+  default, because unindexed searches are generally far more resource intensive.
+  If your application needs to use a filter that results in an unindexed search,
+  then work with your directory administrator to find a solution, such as having
+  the directory maintain the indexes required by your application.</para>
+
   <para>Furthermore, always use <literal>&amp;</literal> with
   <literal>!</literal> to restrict the potential result set before returning
   all entries that do not match part of the filter. For example, <literal
   >(&amp;(location=Oslo)(!(mail=birthday.girl@example.com)))</literal>.</para>
  </section>
- 
+
  <section xml:id="make-modifications-specific">
   <title>Make Modifications Specific</title>
 
-  <para>TODO</para>
+  <para>When you modify attributes with multiple values, for example when you
+  modify a list of group members, replace or delete specific values
+  individually, rather than replacing the entire list of values. Making
+  modifications specific helps directory servers replicate your changes more
+  effectively.</para>
  </section>
- 
+
  <section xml:id="trust-result-codes">
   <title>Trust Result Codes</title>
-  <para>TODO</para>
+
+  <para>Trust the LDAP result code that your application gets from the
+  directory server. For example, if you request a modify application and you
+  get <literal>ResultCode.SUCCESS</literal>, then consider the operation a
+  success rather than issuing a search immediately to get the modified
+  entry.</para>
+
+  <para>The LDAP replication model is loosely convergent. In other words,
+  the directory server can, and probably does, send you
+  <literal>ResultCode.SUCCESS</literal> before replicating your change to
+  every directory server instance across the network. If you issue a read
+  immediately after a write, and a load balancer sends your request to another
+  directory server instance, you could get a result that differs from what
+  you expect.</para>
+
+  <para>The loosely convergent model also means that the entry could have
+  changed since you read it. If needed, you can use <link xlink:show="new"
+  xlink:href="http://tools.ietf.org/html/rfc4528">LDAP assertions</link> to set
+  conditions for your LDAP operations.</para>
  </section>
- 
- <section xml:id="limit-dealings-with-groups">
-  <title>Limit Dealings With Groups</title>
-  <para>TODO</para>
+
+ <section xml:id="ismemberof-for-membership">
+  <title>Check Group Membership on the Account, Not the Group</title>
+
+  <para>If you need to determine which groups an account belongs to, request
+  <literal>isMemberOf</literal> for example with OpenDJ when you read the
+  account entry. Other directory servers use other names for this attribute
+  that identifies the groups to which an account belongs.</para>
  </section>
- 
- <section xml:id="read-the-dse">
-  <title>Read the DSE</title>
-  <para>TODO</para>
+
+ <section xml:id="as-directory-what-it-supports">
+  <title>Ask the Directory Server What It Supports</title>
+
+  <para>Directory servers expose their capabilities, suffixes they support,
+  and so forth as attribute values on the root DSE. See the section on
+  <link xlink:href="dev-guide#read-root-dse"
+  xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Reading Root
+  DSEs</citetitle></link>.</para>
+
+  <para>This allows your application to discover a variety of information at
+  run time, rather than storing configuration separately. Thus putting effort
+  into querying the directory about its configuration and the features it
+  supports can make your application easier to deploy and to maintain.</para>
+
+  <para>For example, rather than hard-coding
+  <literal>dc=example,dc=com</literal> as a suffix DN in your configuration,
+  you can search the root DSE on OpenDJ for <literal>namingContexts</literal>,
+  and then search under the naming context DNs to locate the entries you are
+  looking for in order to initialize your configuration.</para>
+
+  <para>Directory servers also expose their schema over LDAP. The root DSE
+  attribute <literal>subschemaSubentry</literal> shows the DN of the entry
+  holding LDAP schema definitions. See the section, <link
+  xlink:href="dev-guide#get-schema-information"
+  xlink:role="http://docbook.org/xlink/role/olink"><citetitle>Getting Schema
+  Information</citetitle></link>. Note that LDAP object class and attribute
+  type names are case-insensitive, so <literal>isMemberOf</literal> and
+  <literal>ismemberof</literal> refer to the same attribute for example.</para>
  </section>
- 
- <section xml:id="limit-resource-use">
-  <title>Use Resource-intensive Features Sparingly</title>
-  <para>TODO</para>
+
+ <section xml:id="storing-large-attributes">
+  <title>Store Large Attribute Values By Reference</title>
+
+  <para>When you use large attribute values such as photos or audio messages,
+  consider storing the objects themselves elsewhere and keeping only a reference
+  to external content on directory entries. In order to serve results quickly
+  with high availability, directory servers both cache content and also
+  replicate it everywhere.</para>
+
+  <para>Textual entries with a bunch of attributes and perhaps a certificate
+  are often no larger than a few KB. Your directory administrator might
+  therefore be disappointed to learn that your popular application stores
+  users' photo and .mp3 collections as attributes of their accounts.</para>
  </section>
- 
- <section xml:id="avoid-hard-coding">
-  <title>Avoid Hard-coding Certain Information</title>
-  <para>TODO</para>
+
+ <section xml:id="careful-with-persistent-search-and-server-side-sorting">
+  <title>Take Care With Persistent Search &amp; Server-Side Sorting</title>
+
+  <para>A persistent search lets your application receive updates from the
+  server as they happen by keeping the connection open and forcing the server
+  to check whether to return additional results any time it performs a
+  modification in the scope of your search. Directory administrators therefore
+  might hesitate to grant persistent search access to your application.
+  Directory servers like OpenDJ can let you discover updates with less
+  overhead by searching the change log periodically. If you do have to use
+  a persistent search instead, try to narrow the scope of your search.</para>
+
+  <para>Directory servers also support a resource-intensive operation called
+  server-side sorting. When your application requests a server-side sort, the
+  directory server retrieves all the entries matching your search, and then
+  returns the whole set of entries in sorted order. For result sets of any size
+  server-side sorting therefore ties up server resources that could be used
+  elsewhere. Alternatives include both sorting the results after your
+  application receives them, and also working with the directory administrator
+  to have appropriate browsing (virtual list view) indexes maintained on the
+  directory server for applications that must regularly page through long
+  lists of search results.</para>
  </section>
- 
+
  <section xml:id="reuse-schemas">
   <title>Reuse Schemas Where Possible</title>
-  <para>TODO</para>
+
+  <para>Directory servers like OpenDJ come with schema definitions for a wide
+  range of standard object classes and attribute types. This is because
+  directories are designed to be shared by many applications. Directories
+  use unique, typically <link xlink:href="http://www.iana.org/"
+  xlink:show="new">IANA</link>-registered object identifiers (OID) to avoid
+  object class and attribute type name clashes. The overall goal is
+  Internet-wide interoperability.</para>
+
+  <para>You therefore should reuse schema definitions that already exist
+  whenever you reasonably can. Reuse them as is. Do not try to redefine
+  existing schema definitions.</para>
+
+  <para>If you must add schema definitions for your application, extend
+  existing object classes with AUXILIARY classes of your own. Take care to
+  name your definitions such that they do not clash with other names.</para>
+
+  <para>When you have defined schema required for your application, work with
+  the directory administrator to have your definitions added to the directory
+  service. Directory servers like OpenDJ let directory administrators update
+  schema definitions over LDAP, so there is not generally a need to interrupt
+  the service to add your application. Directory administrators can however
+  have other reasons why they hesitate to add your schema definitions.
+  Coming to the discussion prepared with good schema definitions, explanations
+  of why they should be added, and evident regard for interoperability makes
+  it easier for the directory administrator to grant your request.</para>
  </section>
- 
+
  <section xml:id="handle-referrals">
   <title>Handle Referrals</title>
-  <para>TODO</para>
+
+  <para>When a directory server returns a search result, the result is not
+  necessarily an entry. If the result is a referral, then your application
+  should follow up with an additional search based on the URIs provided in
+  the result.</para>
  </section>
- 
- <section xml:id="directory-not-relational-db">
-  <title>Treat a Directory as a Directory</title>
-  <para>TODO</para>
- </section>
- 
+
  <section xml:id="check-result-codes">
   <title>Troubleshooting: Check Result Codes</title>
-  <para>TODO</para>
+
+  <para>LDAP result codes are standard and clearly defined. When you receive
+  a <literal>Result</literal>, check the <literal>ResultCode</literal> value to
+  determine what action your application should take. When the result is not
+  what you expect, you can also read or at least log the message string from
+  <literal>ResultCode.getDiagnosticMessage()</literal>.</para>
  </section>
- 
+
  <section xml:id="check-log-files">
   <title>Troubleshooting: Check Server Log Files</title>
-  <para>TODO</para>
+
+  <para>If you can read the directory server access log, then you can check
+  what the server did with your application's request. For example, the
+  following OpenDJ access log excerpt shows a successful connection from
+  <literal>cn=My Killer App,ou=Apps,dc=example,dc=com</literal> performing
+  a simple bind after Start TLS, and then a simple search before unbind.
+  The lines are wrapped for readability, whereas in the log each record starts
+  with the time stamp.</para>
+
+  <programlisting language="none">[20/Apr/2012:13:31:05 +0200] CONNECT conn=5
+ from=127.0.0.1:51561 to=127.0.0.1:1389 protocol=LDAP
+[20/Apr/2012:13:31:05 +0200] EXTENDED REQ conn=5 op=0 msgID=1 name="StartTLS"
+ oid="1.3.6.1.4.1.1466.20037"
+[20/Apr/2012:13:31:05 +0200] EXTENDED RES conn=5 op=0 msgID=1 name="StartTLS"
+ oid="1.3.6.1.4.1.1466.20037" result=0 etime=0
+[20/Apr/2012:13:31:07 +0200] BIND REQ conn=5 op=1 msgID=2 version=3 type=SIMPLE
+ dn="cn=My Killer App,ou=Apps,dc=example,dc=com"
+[20/Apr/2012:13:31:07 +0200] BIND RES conn=5 op=1 msgID=2 result=0
+ authDN="cn=My Killer App,ou=Apps,dc=example,dc=com" etime=1
+[20/Apr/2012:13:31:07 +0200] SEARCH REQ conn=5 op=2 msgID=3
+ base="dc=example,dc=com" scope=wholeSubtree
+ filter="(uid=kvaughan)" attrs="isMemberOf"
+[20/Apr/2012:13:31:07 +0200] SEARCH RES conn=5 op=2 msgID=3
+ result=0 nentries=1 etime=6
+[20/Apr/2012:13:31:07 +0200] UNBIND REQ conn=5 op=3 msgID=4
+[20/Apr/2012:13:31:07 +0200] DISCONNECT conn=5 reason="Client Unbind"</programlisting>
+
+  <para>Notice that each operation type is shown in upper case, and that the
+  server tracks both the connection (<literal>conn=5</literal>), operation
+  (<literal>op=[0-3]</literal>), and message ID (<literal>msgID=[1-4]</literal>)
+  numbers to make it easy to filter records. The <literal>etime</literal> refers
+  to how long the server worked on the request in milliseconds. Result code
+  0 corresponds to <literal>ResultCode.SUCCESS</literal>, as described in
+  <link xlink:href="http://tools.ietf.org/html/rfc4511#section-4.1.9"
+  xlink:show="new">RFC 4511</link>.</para>
  </section>
- 
- <section xml:id="inspect-network-packets">
-  <title>Troubleshooting: Inspect Network Packets</title>
-  <para>TODO</para>
+
+ <section xml:id="inspect-network-traffic">
+  <title>Troubleshooting: Inspect Network Traffic</title>
+
+  <para>If result codes and server logs are not enough, many network tools
+  can interpret LDAP packets. Get the necessary certificates to decrypt
+  encrypted packet content.</para>
  </section>
 </chapter>

--
Gitblit v1.10.0