mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Mark Craig
20.23.2012 d20452c6fe3b31658aec41ae3bb6ce53dc9f0f1b
More advice for LDAP application developers
1 files modified
228 ■■■■ changed files
opendj3/src/main/docbkx/dev-guide/chap-best-practices.xml 228 ●●●● patch | view | raw | blame | history
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>