Zend_Ldap-Introduction.xml 14 KB

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