[ZF-10089] Zend_Log
[zend.git] / documentation / manual / en / module_specs / Zend_Ldap-Introduction.xml
blob2ca9a73003f4de6ea4df047145651359f39fd030
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!-- Reviewed: no -->
3 <sect1 id="zend.ldap.introduction">
4     <title>Introduction</title>
6     <para>
7         <classname>Zend_Ldap</classname> is a class for performing <acronym>LDAP</acronym>
8         operations including but not limited to binding, searching and modifying entries
9         in an <acronym>LDAP</acronym> directory.
10     </para>
12     <sect2 id="zend.ldap.introduction.theory-of-operations">
13         <title>Theory of operation</title>
15         <para>
16             This component currently consists of the main <classname>Zend_Ldap</classname> class,
17             that conceptually represents a binding to a single <acronym>LDAP</acronym> server
18             and allows for executing operations against a <acronym>LDAP</acronym> server such
19             as OpenLDAP or ActiveDirectory (AD) servers. The parameters for binding may be
20             provided explicitly or in the form of an options array.
21             <classname>Zend_Ldap_Node</classname> provides an object-oriented interface
22             for single <acronym>LDAP</acronym> nodes and can be used to form a basis for an
23             active-record-like interface for a <acronym>LDAP</acronym>-based domain model.
24         </para>
26         <para>
27             The component provides several helper classes to perform operations on
28             <acronym>LDAP</acronym> entries (<classname>Zend_Ldap_Attribute</classname>) such as
29             setting and retrieving attributes (date values, passwords, boolean values, ...), to
30             create and modify <acronym>LDAP</acronym> filter strings
31             (<classname>Zend_Ldap_Filter</classname>) and to manipulate <acronym>LDAP</acronym>
32             distinguished names (DN) (<classname>Zend_Ldap_Dn</classname>).
33         </para>
35         <para>
36             Additionally the component abstracts <acronym>LDAP</acronym> schema browsing
37             for OpenLDAP and ActiveDirectoy servers <classname>Zend_Ldap_Node_Schema</classname>
38             and server information retrieval for OpenLDAP-, ActiveDirectory- and Novell
39             eDirectory servers (<classname>Zend_Ldap_Node_RootDse</classname>).
40         </para>
42         <para>
43             Using the <classname>Zend_Ldap</classname> class depends on the type of
44             <acronym>LDAP</acronym> server and is best summarized with some simple examples.
45         </para>
47         <para>
48             If you are using OpenLDAP, a simple example looks like the following
49             (note that the <emphasis>bindRequiresDn</emphasis> option is important if you are
50             <emphasis>not</emphasis> using AD):
51         </para>
53         <programlisting language="php"><![CDATA[
54 $options = array(
55     'host'              => 's0.foo.net',
56     'username'          => 'CN=user1,DC=foo,DC=net',
57     'password'          => 'pass1',
58     'bindRequiresDn'    => true,
59     'accountDomainName' => 'foo.net',
60     'baseDn'            => 'OU=Sales,DC=foo,DC=net',
62 $ldap = new Zend_Ldap($options);
63 $acctname = $ldap->getCanonicalAccountName('abaker',
64                                            Zend_Ldap::ACCTNAME_FORM_DN);
65 echo "$acctname\n";
66 ]]></programlisting>
68         <para>
69             If you are using Microsoft AD a simple example is:
70         </para>
72         <programlisting language="php"><![CDATA[
73 $options = array(
74     'host'                   => 'dc1.w.net',
75     'useStartTls'            => true,
76     'username'               => 'user1@w.net',
77     'password'               => 'pass1',
78     'accountDomainName'      => 'w.net',
79     'accountDomainNameShort' => 'W',
80     'baseDn'                 => 'CN=Users,DC=w,DC=net',
82 $ldap = new Zend_Ldap($options);
83 $acctname = $ldap->getCanonicalAccountName('bcarter',
84                                            Zend_Ldap::ACCTNAME_FORM_DN);
85 echo "$acctname\n";
86 ]]></programlisting>
88         <para>
89             Note that we use the <methodname>getCanonicalAccountName()</methodname> method
90             to retrieve the account DN here only because that is what exercises the
91             most of what little code is currently present in this class.
92         </para>
94         <sect3 id="zend.ldap.introduction.theory-of-operations.automatic-username-canonicalization">
95             <title>Automatic Username Canonicalization When Binding</title>
97             <para>
98                 If <methodname>bind()</methodname> is called with a non-DN username but
99                 <emphasis>bindRequiresDN</emphasis> is <constant>TRUE</constant> and no username in
100                 DN form was supplied as an option, the bind will fail. However, if a username in DN
101                 form is supplied in the options array, <classname>Zend_Ldap</classname> will
102                 first bind with that username, retrieve the account DN for the username
103                 supplied to <methodname>bind()</methodname> and then re-bind with that DN.
104             </para>
106             <para>
107                 This behavior is critical to <link
108                     linkend="zend.auth.adapter.ldap"><classname>Zend_Auth_Adapter_Ldap</classname></link>,
109                 which passes the username supplied by the user directly to
110                 <methodname>bind()</methodname>.
111             </para>
113             <para>
114                 The following example illustrates how the non-DN username
115                 '<emphasis>abaker</emphasis>' can be used with <methodname>bind()</methodname>:
116             </para>
118             <programlisting language="php"><![CDATA[
119 $options = array(
120         'host'              => 's0.foo.net',
121         'username'          => 'CN=user1,DC=foo,DC=net',
122         'password'          => 'pass1',
123         'bindRequiresDn'    => true,
124         'accountDomainName' => 'foo.net',
125         'baseDn'            => 'OU=Sales,DC=foo,DC=net',
127 $ldap = new Zend_Ldap($options);
128 $ldap->bind('abaker', 'moonbike55');
129 $acctname = $ldap->getCanonicalAccountName('abaker',
130                                            Zend_Ldap::ACCTNAME_FORM_DN);
131 echo "$acctname\n";
132 ]]></programlisting>
134             <para>
135                 The <methodname>bind()</methodname> call in this example sees that
136                 the username '<emphasis>abaker</emphasis>' is not in DN form, finds
137                 <emphasis>bindRequiresDn</emphasis> is <constant>TRUE</constant>, uses
138                 '<command>CN=user1,DC=foo,DC=net</command>' and '<emphasis>pass1</emphasis>' to
139                 bind, retrieves the DN for '<emphasis>abaker</emphasis>', unbinds and then rebinds
140                 with the newly discovered
141                 '<command>CN=Alice Baker,OU=Sales,DC=foo,DC=net</command>'.
142             </para>
143         </sect3>
145         <sect3 id="zend.ldap.introduction.theory-of-operations.account-name-canonicalization">
146             <title>Account Name Canonicalization</title>
148             <para>
149                 The <emphasis>accountDomainName</emphasis> and
150                 <emphasis>accountDomainNameShort</emphasis>
151                 options are used for two purposes: (1) they facilitate multi-domain
152                 authentication and failover capability, and (2) they are also used to
153                 canonicalize usernames. Specifically, names are canonicalized to the
154                 form specified by the <emphasis>accountCanonicalForm</emphasis> option.
155                 This option may one of the following values:
156             </para>
158             <table id="zend.ldap.using.theory-of-operation.account-name-canonicalization.table">
159                 <title>Options for accountCanonicalForm</title>
161                 <tgroup cols="3">
162                     <thead>
163                         <row>
164                             <entry>Name</entry>
165                             <entry>Value</entry>
166                             <entry>Example</entry>
167                         </row>
168                     </thead>
170                     <tbody>
171                         <row>
172                             <entry><constant>ACCTNAME_FORM_DN</constant></entry>
173                             <entry>1</entry>
174                             <entry>CN=Alice Baker,CN=Users,DC=example,DC=com</entry>
175                         </row>
177                         <row>
178                             <entry><constant>ACCTNAME_FORM_USERNAME</constant></entry>
179                             <entry>2</entry>
180                             <entry>abaker</entry>
181                         </row>
183                         <row>
184                             <entry><constant>ACCTNAME_FORM_BACKSLASH</constant></entry>
185                             <entry>3</entry>
186                             <entry>EXAMPLE\abaker</entry>
187                         </row>
189                         <row>
190                             <entry><constant>ACCTNAME_FORM_PRINCIPAL</constant></entry>
191                             <entry>4</entry>
192                             <entry><filename>abaker@example.com</filename></entry>
193                         </row>
194                     </tbody>
195                 </tgroup>
196             </table>
198             <para>
199                 The default canonicalization depends on what account domain name options
200                 were supplied. If <emphasis>accountDomainNameShort</emphasis> was supplied, the
201                 default <emphasis>accountCanonicalForm</emphasis> value is
202                 <constant>ACCTNAME_FORM_BACKSLASH</constant>. Otherwise, if
203                 <emphasis>accountDomainName</emphasis> was supplied, the
204                 default is <constant>ACCTNAME_FORM_PRINCIPAL</constant>.
205             </para>
207             <para>
208                 Account name canonicalization ensures that the string used to identify
209                 an account is consistent regardless of what was supplied to
210                 <methodname>bind()</methodname>. For example, if the user supplies an account
211                 name of <filename>abaker@example.com</filename> or just
212                 <emphasis>abaker</emphasis> and the <emphasis>accountCanonicalForm</emphasis>
213                 is set to 3, the resulting canonicalized name would be
214                 <emphasis>EXAMPLE\abaker</emphasis>.
215             </para>
216         </sect3>
218         <sect3 id="zend.ldap.introduction.theory-of-operations.multi-domain-failover">
219             <title>Multi-domain Authentication and Failover</title>
221             <para>
222                 The <classname>Zend_Ldap</classname> component by itself makes no attempt
223                 to authenticate with multiple servers. However, <classname>Zend_Ldap</classname>
224                 is specifically designed to handle this scenario gracefully. The
225                 required technique is to simply iterate over an array of arrays of serve
226                  options and attempt to bind with each server. As described above
227                  <methodname>bind()</methodname> will automatically canonicalize each name, so
228                 it does not matter if the user passes <filename>abaker@foo.net</filename> or
229                 <emphasis>W\bcarter</emphasis> or <emphasis>cdavis</emphasis> - the
230                 <methodname>bind()</methodname> method will only succeed if the credentials were
231                 successfully used in the bind.
232             </para>
234             <para>
235                 Consider the following example that illustrates the technique required to
236                 implement multi-domain authentication and failover:
237             </para>
239             <programlisting language="php"><![CDATA[
240 $acctname = 'W\\user2';
241 $password = 'pass2';
243 $multiOptions = array(
244     'server1' => array(
245         'host'                   => 's0.foo.net',
246         'username'               => 'CN=user1,DC=foo,DC=net',
247         'password'               => 'pass1',
248         'bindRequiresDn'         => true,
249         'accountDomainName'      => 'foo.net',
250         'accountDomainNameShort' => 'FOO',
251         'accountCanonicalForm'   => 4, // ACCT_FORM_PRINCIPAL
252         'baseDn'                 => 'OU=Sales,DC=foo,DC=net',
253     ),
254     'server2' => array(
255         'host'                   => 'dc1.w.net',
256         'useSsl'                 => true,
257         'username'               => 'user1@w.net',
258         'password'               => 'pass1',
259         'accountDomainName'      => 'w.net',
260         'accountDomainNameShort' => 'W',
261         'accountCanonicalForm'   => 4, // ACCT_FORM_PRINCIPAL
262         'baseDn'                 => 'CN=Users,DC=w,DC=net',
263     ),
266 $ldap = new Zend_Ldap();
268 foreach ($multiOptions as $name => $options) {
270     echo "Trying to bind using server options for '$name'\n";
272     $ldap->setOptions($options);
273     try {
274         $ldap->bind($acctname, $password);
275         $acctname = $ldap->getCanonicalAccountName($acctname);
276         echo "SUCCESS: authenticated $acctname\n";
277         return;
278     } catch (Zend_Ldap_Exception $zle) {
279         echo '  ' . $zle->getMessage() . "\n";
280         if ($zle->getCode() === Zend_Ldap_Exception::LDAP_X_DOMAIN_MISMATCH) {
281             continue;
282         }
283     }
285 ]]></programlisting>
287             <para>
288                 If the bind fails for any reason, the next set of server options is tried.
289             </para>
291             <para>
292                 The <methodname>getCanonicalAccountName()</methodname> call gets the canonical
293                 account name that the application would presumably use to associate data with such
294                 as preferences. The <emphasis>accountCanonicalForm = 4</emphasis> in all server
295                 options ensures that the canonical form is consistent regardless of which
296                 server was ultimately used.
297             </para>
299             <para>
300                 The special <constant>LDAP_X_DOMAIN_MISMATCH</constant> exception occurs when an
301                 account name with a domain component was supplied (e.g.,
302                 <filename>abaker@foo.net</filename> or <emphasis>FOO\abaker</emphasis> and not just
303                 <emphasis>abaker</emphasis>) but the domain component did not match either domain
304                 in the currently selected server options. This exception indicates
305                 that the server is not an authority for the account. In this
306                 case, the bind will not be performed, thereby eliminating unnecessary
307                 communication with the server. Note that the <emphasis>continue</emphasis>
308                 instruction has no effect in this example, but in practice for error handling and
309                 debugging purposes, you will probably want to check for
310                 <constant>LDAP_X_DOMAIN_MISMATCH</constant> as well as
311                 <constant>LDAP_NO_SUCH_OBJECT</constant> and
312                 <constant>LDAP_INVALID_CREDENTIALS</constant>.
313             </para>
315             <para>
316                 The above code is very similar to code used within <link
317                     linkend="zend.auth.adapter.ldap"><classname>Zend_Auth_Adapter_Ldap</classname></link>.
318                 In fact, we recommend that you simply use that authentication adapter for
319                 multi-domain + failover <acronym>LDAP</acronym> based authentication
320                 (or copy the code).
321             </para>
322         </sect3>
323     </sect2>
324 </sect1>