зеркало из https://github.com/mozilla/pjs.git
379 строки
20 KiB
Plaintext
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, &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>&
|
|
</literal>, for example <literal>(&(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 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>
|