pjs/directory/docs/ldapjdk/best-practices.sgm

379 строки
20 KiB
Plaintext

<!--
Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
Portions copyright 1999 Netscape Communications Corporation. All
Rights Reserved.
The contents of this document are subject to the terms of the
Creative Commons Attribution-ShareAlike 2.5 license or any later
version (the "License"). You may not use this document except in
compliance with the License.
See the License for the specific language governing
permissions and limitations under the License. You can obtain
a copy of the License at
http://creativecommons.org/licenses/by-sa/2.5/legalcode.
-->
<chapter id="best-practices-overview"><title>Best Practices for
Writing Client Applications</title>
<highlights>
<itemizedlist>
<para>This chapter includes best practices for developing client applications
and cover the following topics:</para>
<listitem><para><olink targetptr="best-ldapv3">Creating Your Application</olink></para>
</listitem>
<listitem><para><olink targetptr="best-troubleshooting">Troubleshooting Problems</olink></para>
</listitem>
</itemizedlist>
</highlights>
<sect1 id="best-ldapv3"><title>Creating Your Application</title>
<itemizedlist>
<para>This section identifies practices to observe when creating directory
client applications.</para>
<listitem><para><olink targetptr="specify-ldapv3">Specify LDAP v3</olink></para>
</listitem>
<listitem><para><olink targetptr="best-authenticating">Authenticate Correctly</olink></para>
</listitem>
<listitem><para><olink targetptr="best-connect">Limit Connection Overhead</olink></para>
</listitem>
<listitem><para><olink targetptr="best-inactivity-timeouts">Handle Potential
Inactivity Timeouts</olink></para></listitem>
<listitem><para><olink targetptr="best-entries">Retrieve Entries Intelligently</olink></para>
</listitem>
<listitem><para><olink targetptr="best-filters">Write Simple, Conforming LDAP
Filters</olink></para></listitem>
<listitem><para><olink targetptr="best-mods">Performing Specific Modifications</olink></para>
</listitem>
<listitem><para><olink targetptr="best-rc">Trust Result Codes</olink></para>
</listitem>
<listitem><para><olink targetptr="best-groups">Limit Dealings With Groups
and Roles</olink></para></listitem>
<listitem><para><olink targetptr="best-dse">Read the DSE</olink></para>
</listitem>
<listitem><para><olink targetptr="best-resources">Use Resource-Intensive Features Sparingly</olink></para></listitem>
<listitem><para><olink targetptr="best-dit">Avoid Hard Coding Certain Information
</olink></para></listitem>
<listitem><para><olink targetptr="best-schema">Define Schemas Only When Necessary
</olink></para></listitem>
<listitem><para><olink targetptr="best-refer">Handle Referrals</olink></para>
</listitem>
<listitem><para><olink targetptr="best-not-rdbm">Treat a Directory as a Directory
</olink></para></listitem>
</itemizedlist>
<sect2 id="specify-ldapv3"><title>Specify LDAP v3</title>
<itemizedlist>
<para>Many client libraries default to LDAP v2, but you can elect to use LDAP
v3. To benefit from LDAP v3 features, you can set up the connection, and then
authenticate explicitly using LDAP v3.</para>
<listitem><para>With JNDI, you could use LDAP v3 as shown here.</para>
<programlisting role="fragment">import java.util.Hashtable;
import javax.naming.ldap.InitialLdapContext;
Hashtable env = new Hashtable();
env.put("java.naming.ldap.version", "<emphasis>3</emphasis>");
InitialLdapContext ctx = new InitialLdapContext(env, null);</programlisting>
</listitem>
<listitem><para>With &DirectorySDKForC;, you could use LDAP v3 as shown
here.</para>
<programlisting role="fragment">#include "ldap.h"
int version = <emphasis>LDAP_VERSION3</emphasis>;
ldap_set_option( NULL, LDAP_OPT_PROTOCOL_VERSION, &amp;version );</programlisting>
<para>&DirectorySDKForC; uses LDAP v3 by default.</para></listitem>
<listitem><para>With &DirectorySDKForJava;, you could use LDAP v3 as shown
here.</para>
<programlisting role="fragment">import netscape.ldap.LDAPConnection;
LDAPConnection ld = new LDAPConnection();
ld.setOption(LDAPv3.PROTOCOL_VERSION, new Integer(<emphasis>3</emphasis>));
</programlisting>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="best-authenticating"><title>Authenticate Correctly</title>
<para>Your SDK uses terminology that is slightly different from LDAP v3. In
LDAP v3, you connect, then you bind and perform LDAP operations, then you
unbind and disconnect. The bind is the authentication operation in LDAP. Your
application can hold onto a connection but change the authentication credentials
by using the bind operation again.</para>
<para>Some directories do not allow anonymous access, even for reads. When
you build your application, keep the option that allows users to authenticate
to the directory. Furthermore, the information sent across the network can
be sensitive. You can protect sensitive data by allowing the application to
secure the connection by using Secure Sockets Layer (SSL) or Start Transport
Layer Security (TLS).</para>
<para>If your application needs to authenticate, obtain a regular account
to authenticate with the directory, rather than using the directory superuser
account such as <literal>cn=Directory Manager</literal>. When you authenticate
as directory superuser, you often bypass normal access control mechanisms.
Bypassing normal access control renders auditing directory access more difficult.
</para>
<para>When authenticating, have your application use SSL or SASL DIGEST MD5
to avoid sending passwords over the network in clear text. Furthermore, when
using password-based authentication, have your application check password
policy controls, especially to determine when a password must be renewed.</para>
</sect2>
<sect2 id="best-connect"><title>Limit Connection Overhead</title>
<para>A new connection requires system resources. The LDAP model allows you
to reuse connections by binding again with a different identity on the same
connection. Thus, you can avoid the costs of new connections, particularly
negotiated connections such as connections that use SSL, by reusing connections.
Your application can use a pool of connections, rebinding when necessary.
Your application can alternatively use the proxy authorization control to
remain authenticated as the application but perform operations on behalf of
a particular user.</para>
<para>When establishing a connection, your application can provide alternate
server host names and port numbers to facilitate failover that is transparent
to the application. You can also set time limits for LDAP operations to avoid
getting blocked.</para>
<para>When finished with a connection, your application should perform an
unbind.</para></sect2>
<sect2 id="best-inactivity-timeouts"><title>Handle Potential Inactivity Timeouts</title>
<para>Most network equipment can use timeouts to drop stale connections, ensuring
the equipment keeps a maximum number of connections that are available.</para>
<para>If your application pools connections or opens connections for persistent
search, than guard against timeouts that drop those connections. Use the connections
occasionally to reset inactivity timers present in the network.</para>
<para>Alternatively, if you have control over the connection, consider disabling
inactivity time outs for your applications that need to keep persistent connections
open. Load balancers and proxy software often use inactivity timeouts.</para>
</sect2>
<sect2 id="best-entries"><title>Retrieve Entries Intelligently</title>
<para>&cnDirectoryServer; typically responds quickly to requests for entries.
Yet, &cnDirectoryServer; can respond most quickly when your application asks
it to do only necessary work. If you need to read only a few attributes in
an entry, request each attribute explicitly. Avoid reading the entire entry,
then parsing the entire entry to obtain the required data.</para>
<para>Furthermore, when you do request attributes in an entry, retrieve all
the required attributes at once. Each new request involves a new operation
on the server.</para>
<para>If any of the attributes that you require are operational attributes,
you must request those attributes specifically. Such attributes are identifiable
in directory schema by their <literal>USAGE</literal>, which is <literal>directoryOperation
</literal> or <literal>dsaOperation</literal>.</para>
<para>When retrieving entries and attributes, recognize that you might not
have access to all the attributes that exist.</para></sect2>
<sect2 id="best-filters"><title>Write Simple, Conforming LDAP Filters</title>
<indexterm>
<primary>LDAP filters</primary>
<secondary>writing</secondary>
</indexterm>
<para>The best filters use attributes that are indexed according to the way
the attributes are indexed. For example, if <literal>employeeNumber</literal> is
indexed for equality, your filter should be an equality filter such as <literal>(employeeNumber=123456)
</literal>. Do not use a substring filter instead.</para>
<para>Avoid deeply nested complex filters when you can. When you must use
complex filters, place the most specific filters first to narrow the list
of candidate entries the directory must check. For best results, use <literal>not
</literal>, <literal>!</literal>, only with <literal>and</literal>, <literal>&amp;
</literal>, for example <literal>(&amp;(cn=Barbara)(!(sn=Jensen)))</literal>.
When you use <literal>not</literal> with <literal>or</literal> in a filter,
the directory must construct a candidate list of everything except what your
filter specifies.</para></sect2>
<sect2 id="best-mods"><title>Performing Specific Modifications</title>
<para>Modifications are atomic on the entry to which the modifications apply.
When modifying multivalued attributes, delete and replace specific values.
Do not replace an entire list of multiple values to change only a few values.
Replacing specific values is particularly good practice when the changes must
be replicated across a set of servers.</para>
<para>Moreover, when you have large values to store in an attribute, store
a reference to the data instead of storing the data object.</para></sect2>
<sect2 id="best-rc"><title>Trust Result Codes</title>
<indexterm>
<primary>result codes</primary>
<secondary>trust</secondary>
</indexterm>
<para>&cnDirectoryServer; trades tight consistency across replica servers
for very high performance, availability, and scalability. By allowing <firstterm>
loose consistency</firstterm> of data across sets of replica servers, &cnDirectoryServer; instances
can respond very quickly to your application. Yet, data replication is not
instantaneous. A short but detectable delay can ensue after a server returns
success for a write operation, but before the effects are seen on other replicas.
</para>
<para>Therefore, when your application receives a result code from &cnDirectoryServer; to
indicate that an operation was successful, your application should trust the
result code. When application requests are balanced across replicas, reading
from another replica might result in errors due to a slight delay in replication.
</para></sect2>
<sect2 id="best-groups"><title>Limit Dealings With Groups and Roles</title>
<indexterm>
<primary>groups</primary>
<secondary>working with</secondary>
</indexterm>
<para>When you want to know whether an account belongs to a group or a role,
read only the necessary attribute values. Do not read the entire list of group
members.</para>
<orderedlist>
<para>For <literal>dynamic groups</literal>, do the following:</para>
<listitem><para>Read the URL from the group definition.</para></listitem>
<listitem><para>Examine the host, DN, and scope of the URL.</para></listitem>
<listitem><para>Apply the filter part of the URL to the entry for the account.</para>
</listitem>
</orderedlist>
<para><indexterm>
<primary>roles</primary>
<secondary>working with</secondary>
</indexterm>For <literal>roles</literal>, compare the DN of the role to the <literal>
nsRole</literal> attribute of the entry for the account, such as <literal>(nsrole=cn=management,ou=people,dc=example,dc=com)
</literal>. You can then retrieve all the values of the <literal>nsRole</literal> attribute
for the account.</para></sect2>
<sect2 id="best-dse"><title>Read the DSE</title>
<indexterm>
<primary>DSE, root</primary>
<secondary>reading</secondary>
</indexterm>
<para>The root DSE is the entry that is retrieved by <command>ldapsearch -b
"" -s base "(objectclass=*)"</command>. The root DSE describes server capabilities.
The root DSE contains information about supported LDAP protocol versions,
naming contexts (suffixes), LDAP v3 controls, LDAP v3 extensions, and authentication
mechanisms. The root DSE can contain information about the server version.</para>
<para>Some directory administrators protect access to the root DSE. Yet, applications
might read the root DSE to confirm that the server in fact supports functionality
required by applications.</para></sect2>
<sect2 id="best-resources"><title>Use Resource-Intensive Features Sparingly</title>
<para>Directories offer powerful features that can nevertheless place a heavy
load on the server. Two such features are persistent search, and server-side
sorting.</para>
<para>Persistent search lets you start a search that does not stop when complete,
but instead allows you to receive updates when entries are modified. To provide
this feature, the server must handle your search when anything happens to
an entry in its scope.</para>
<para>Server-side sorting requires that the server sort the entries that are
returned during a search. Instead of returning entries as quickly as possible,
the server must therefore get the list to return, and sort the list.</para>
</sect2>
<sect2 id="best-dit"><title>Avoid Hard Coding Certain Information</title>
<para>The container entry for a subtree might be not be identical on different
directories. Rather than hard code the container entry throughout your application,
locate the container entry. Then navigate beneath the container entry in the
tree.</para>
<para>Object classes and attribute types for the same information can also
differ from directory to directory. Use configuration files, properties files,
or other easily modifiable variables rather than hard coding object class
and attribute type identifiers into your application.</para>
<para>Be aware as well that object class and attribute type identifiers are <emphasis>
not case-sensitive</emphasis> in LDAP. Your application should therefore recognize
that <literal>inetOrgPerson</literal> and <literal>inetorgperson</literal> are
equivalent, as are <literal>isMemberOf</literal> and <literal>ismemberof</literal>.
</para></sect2>
<sect2 id="best-schema"><title>Define Schemas Only When Necessary</title>
<indexterm>
<primary>schema</primary>
<secondary>defining</secondary>
</indexterm>
<para>Schemas define the object classes and attribute types that are recognized
by the directory. If your application can use a standard schema, use the standard
schema. &cnDirectoryServer; contains schemas that define numerous standard
object classes, and attribute types.</para>
<itemizedlist>
<para>When you must define your own schema objects, follow these guidelines:</para>
<listitem><para>Extend existing object classes by using <literal>AUXILIARY</literal> classes.
</para></listitem>
<listitem><para>Create new attributes rather than redefining existing attributes.
</para><para>Other applications might depend on existing attributes to keep
their existing semantics.</para></listitem>
<listitem><para>Obtain new object identifiers for the schema elements you
define, rather than reusing existing object identifiers.</para></listitem>
<listitem><para>Obtain new names for the schema elements you define, rather
than reusing existing names.</para></listitem>
<listitem><para>Update &cnDirectoryServer; schema over LDAP if you can.</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="best-refer"><title>Handle Referrals</title>
<indexterm>
<primary>referrals</primary>
<secondary>following</secondary>
</indexterm>
<para>LDAP v3 allows directories that are unable to handle your request to
refer your application to other directories. Your application should follow
those referrals.</para>
<para>When following referrals, realize that authentication procedures might
not be exactly the same on different directories. Also, directories that refer
to each other could potentially cause a referral loop. With &DirectorySDKForC; and &DirectorySDKForJava;,
you can limit referral hops to prevent your application from being referred
endlessly from one directory to another directory. The JNDI interface enables
you to follow referrals automatically.</para></sect2>
<sect2 id="best-not-rdbm"><title>Treat a Directory as a Directory</title>
<para>A directory is typically a repository for identity data, and for information
that you expect to keep for awhile and read often. You might typically find
relational databases better adapted to hold transient data such as session
keys and presence information, or voluminous accumulated data such as application
logs.</para></sect2>
</sect1>
<sect1 id="best-troubleshooting"><title>Troubleshooting Problems</title>
<para>This section covers basic troubleshooting approaches and techniques
to use when developing directory client applications. You can check result
codes, check server log files, and inspect network packets.</para>
<sect2 id="trouble-rc"><title>Check Result Codes</title>
<indexterm>
<primary>troubleshooting</primary>
<secondary>result codes</secondary>
</indexterm>
<para>When an LDAP request from your application fails on the server, the
server sends back a result code, and possibly an explanatory message. Your
application should check the result codes, and for explanatory messages. Common
failure result codes include the following, which are expressed as decimal
values. Others result codes are defined as well.</para>
<variablelist>
<varlistentry><term><errorcode>1</errorcode></term>
<listitem><para>LDAP operations error. The server encountered an error while
processing your request.</para>
</listitem>
</varlistentry>
<varlistentry><term><errorcode>32</errorcode></term>
<listitem><para>No such object. The entry is not present on the server. Also,
no referral is defined for the entry.</para>
</listitem>
</varlistentry>
<varlistentry><term><errorcode>49</errorcode></term>
<listitem><para>Invalid credentials. Your application failed to authenticate
properly.</para>
</listitem>
</varlistentry>
<varlistentry><term><errorcode>53</errorcode></term>
<listitem><para>LDAP unwilling to perform. The directory does not support
the request. Alternatively, the directory is not currently in a state in which
to complete your request. For example, the directory might be in read-only
mode when your application requests a modification.</para>
</listitem>
</varlistentry>
<varlistentry><term><errorcode>65</errorcode></term>
<listitem><para>Object class violation. Your write request would cause an
entry to no longer conform to the schema defined for the directory.</para>
</listitem>
</varlistentry>
<varlistentry><term><errorcode>68</errorcode></term>
<listitem><para>Already exists. Your application is requesting to add an entry
that has the same DN as an entry already present in the directory.</para>
</listitem>
</varlistentry>
</variablelist>
<para><ulink url="http://ietf.org/rfc/rfc4511.txt" type="text_url">RFC 4511</ulink> defines
LDAP error codes, as does <olink targetptr="bdaxx">Chapter&nbsp;22, Directory
SDK for C Result Codes</olink>.</para></sect2>
<sect2 id="trouble-logs"><title>Check Server Log Files</title>
<indexterm>
<primary>troubleshooting</primary>
<secondary>log files</secondary>
</indexterm>
<para>&cnDirectoryServer; logs messages related to server operation in its <filename>
logs/errors</filename> file. If you have access to this file, you might find
useful troubleshooting information there.</para>
<para>When debugging your application against &cnDirectoryServer;, you can
adjust the log level, as well. See the server documentation for instructions.</para>
</sect2>
<sect2 id="trouble-network-tools"><title>Inspect Network Packets</title>
<indexterm>
<primary>troubleshooting</primary>
<secondary>decoding packets</secondary>
</indexterm>
<para>Although LDAP is not a textual protocol, tools such as <citerefentry>
<refentrytitle>snoop</refentrytitle><manvolnum>1M</manvolnum></citerefentry>, <command>
ethereal</command>, and <command>tcpdump</command> can decode the packets,
sometimes providing you with important debugging information.</para></sect2>
</sect1>
</chapter>