2
0

Zend_Ldap.xml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <sect1 id="zend.ldap.using">
  4. <title>Introduction</title>
  5. <note>
  6. <title>Minimal Functionality</title>
  7. <para>
  8. Currently this class is designed only to satisfy the limited functionality necessary for the
  9. <link linkend="zend.auth.adapter.ldap"><classname>Zend_Auth_Adapter_Ldap</classname></link> authentication adapter.
  10. Operations such as searching, creating, modifying or renaming entries in the directory are currently not
  11. supported and will be defined at a later time.
  12. </para>
  13. </note>
  14. <para>
  15. <classname>Zend_Ldap</classname> is a class for performing LDAP operations including but not limited to binding,
  16. searching and modifying entries in an LDAP directory.
  17. </para>
  18. <sect2 id="zend.ldap.using.theory-of-operation">
  19. <title>Theory of Operation</title>
  20. <para>
  21. This component currently consists of two classes, <classname>Zend_Ldap</classname> and
  22. <classname>Zend_Ldap_Exception</classname>. The <classname>Zend_Ldap</classname> class conceptually represents a binding to a
  23. single LDAP server. The parameters for binding may be provided explicitly or in the form of an options
  24. array.
  25. </para>
  26. <para>
  27. Using the <classname>Zend_Ldap</classname> class depends on the type of LDAP server and is best summarized with some
  28. simple examples.
  29. </para>
  30. <para>
  31. If you are using OpenLDAP, a simple example looks like the following (note that the
  32. <code>bindRequiresDn</code> option is important if you are <emphasis>not</emphasis> using AD):
  33. <programlisting language="php"><![CDATA[
  34. $options = array(
  35. 'host' => 's0.foo.net',
  36. 'username' => 'CN=user1,DC=foo,DC=net',
  37. 'password' => 'pass1',
  38. 'bindRequiresDn' => true,
  39. 'accountDomainName' => 'foo.net',
  40. 'baseDn' => 'OU=Sales,DC=foo,DC=net',
  41. );
  42. $ldap = new Zend_Ldap($options);
  43. $acctname = $ldap->getCanonicalAccountName('abaker',
  44. Zend_Ldap::ACCTNAME_FORM_DN);
  45. echo "$acctname\n";
  46. ]]></programlisting>
  47. </para>
  48. <para>
  49. If you are using Microsoft AD a simple example is:
  50. <programlisting language="php"><![CDATA[
  51. $options = array(
  52. 'host' => 'dc1.w.net',
  53. 'useStartTls' => true,
  54. 'username' => 'user1@w.net',
  55. 'password' => 'pass1',
  56. 'accountDomainName' => 'w.net',
  57. 'accountDomainNameShort' => 'W',
  58. 'baseDn' => 'CN=Users,DC=w,DC=net',
  59. );
  60. $ldap = new Zend_Ldap($options);
  61. $acctname = $ldap->getCanonicalAccountName('bcarter',
  62. Zend_Ldap::ACCTNAME_FORM_DN);
  63. echo "$acctname\n";
  64. ]]></programlisting>
  65. Note that we use the <code>getCanonicalAccountName()</code> method to retrieve the account DN here only
  66. because that is what exercises the most of what little code is currently present in this class.
  67. </para>
  68. <sect3 id="zend.ldap.using.theory-of-operation.username-canonicalization-automatic">
  69. <title>Automatic Username Canonicalization When Binding</title>
  70. <para>
  71. If <code>bind()</code> is called with a non-DN username but <code>bindRequiresDN</code> is
  72. <constant>TRUE</constant> and no username in DN form was supplied as an option, the bind will fail. However, if
  73. a username in DN form is supplied in the options array, <classname>Zend_Ldap</classname> will first bind with
  74. that username, retrieve the account DN for the username supplied to <code>bind()</code> and then re-
  75. bind with that DN.
  76. </para>
  77. <para>
  78. This behavior is critical to <classname>Zend_Auth_Adapter_Ldap</classname>, which passes the username supplied by
  79. the user directly to <code>bind()</code>.
  80. </para>
  81. <para>
  82. The following example illustrates how the non-DN username '<code>abaker</code>' can be used with
  83. <code>bind()</code>:
  84. <programlisting language="php"><![CDATA[
  85. $options = array(
  86. 'host' => 's0.foo.net',
  87. 'username' => 'CN=user1,DC=foo,DC=net',
  88. 'password' => 'pass1',
  89. 'bindRequiresDn' => true,
  90. 'accountDomainName' => 'foo.net',
  91. 'baseDn' => 'OU=Sales,DC=foo,DC=net',
  92. );
  93. $ldap = new Zend_Ldap($options);
  94. $ldap->bind('abaker', 'moonbike55');
  95. $acctname = $ldap->getCanonicalAccountName('abaker',
  96. Zend_Ldap::ACCTNAME_FORM_DN);
  97. echo "$acctname\n";
  98. ]]></programlisting>
  99. The <code>bind()</code> call in this example sees that the username '<code>abaker</code>' is not in DN
  100. form, finds <code>bindRequiresDn</code> is <constant>TRUE</constant>, uses
  101. '<code>CN=user1,DC=foo,DC=net</code>' and '<code>pass1</code>' to bind, retrieves the DN for
  102. '<code>abaker</code>', unbinds and then rebinds with the newly discovered
  103. '<code>CN=Alice Baker,OU=Sales,DC=foo,DC=net</code>'.
  104. </para>
  105. </sect3>
  106. <sect3 id="zend.ldap.using.theory-of-operation.options">
  107. <title>Zend_Ldap Options</title>
  108. <para>
  109. The <classname>Zend_Ldap</classname> component accepts an array of options either supplied to the constructor or
  110. through the <code>setOptions()</code> method. The permitted options are as follows:
  111. <table id="zend.ldap.using.theory-of-operation.options.table">
  112. <title>Zend_Ldap Options</title>
  113. <tgroup cols="2">
  114. <thead>
  115. <row>
  116. <entry>Name</entry>
  117. <entry>Description</entry>
  118. </row>
  119. </thead>
  120. <tbody>
  121. <row>
  122. <entry>host</entry>
  123. <entry>
  124. The default hostname of LDAP server if not supplied to <code>connect()</code> (also may be
  125. used when trying to canonicalize usernames in <code>bind()</code>).
  126. </entry>
  127. </row>
  128. <row>
  129. <entry>port</entry>
  130. <entry>
  131. Default port of LDAP server if not supplied to <code>connect()</code>.
  132. </entry>
  133. </row>
  134. <row>
  135. <entry>useStartTls</entry>
  136. <entry>
  137. Whether or not the LDAP client should use TLS (aka SSLv2) encrypted transport. A value of
  138. <constant>TRUE</constant> is strongly favored in production environments to prevent passwords from
  139. be transmitted in clear text. The default value is <constant>FALSE</constant>, as servers
  140. frequently require that a certificate be installed separately after installation.
  141. The <code>useSsl</code> and <code>useStartTls</code> options are mutually exclusive.
  142. The <code>useStartTls</code> option should be favored over <code>useSsl</code> but
  143. not all servers support this newer mechanism.
  144. </entry>
  145. </row>
  146. <row>
  147. <entry>useSsl</entry>
  148. <entry>
  149. Whether or not the LDAP client should use SSL encrypted transport. The <code>useSsl</code>
  150. and <code>useStartTls</code> options are mutually exclusive.
  151. </entry>
  152. </row>
  153. <row>
  154. <entry>username</entry>
  155. <entry>
  156. The default credentials username. Some servers require that this be in DN form.
  157. </entry>
  158. </row>
  159. <row>
  160. <entry>password</entry>
  161. <entry>
  162. The default credentials password (used only with username above).
  163. </entry>
  164. </row>
  165. <row>
  166. <entry>bindRequiresDn</entry>
  167. <entry>
  168. If <constant>TRUE</constant>, this instructs <classname>Zend_Ldap</classname> to retrieve the DN for the
  169. account used to bind if the username is not already in DN form. The default value is
  170. <constant>FALSE</constant>.
  171. </entry>
  172. </row>
  173. <row>
  174. <entry>baseDn</entry>
  175. <entry>
  176. The default base DN used for searching (e.g., for accounts). This option is required for
  177. most account related operations and should indicate the DN under which accounts are
  178. located.
  179. </entry>
  180. </row>
  181. <row>
  182. <entry>accountCanonicalForm</entry>
  183. <entry>
  184. A small integer indicating the form to which account names should be canonicalized. See the
  185. <emphasis>Account Name Canonicalization</emphasis> section below.
  186. </entry>
  187. </row>
  188. <row>
  189. <entry>accountDomainName</entry>
  190. <entry>
  191. The FQDN domain for which the target LDAP server is an authority (e.g., example.com).
  192. </entry>
  193. </row>
  194. <row>
  195. <entry>accountDomainNameShort</entry>
  196. <entry>
  197. The 'short' domain for which the target LDAP server is an authority. This is usually used
  198. to specify the NetBIOS domain name for Windows networks but may also be used by non-AD
  199. servers.
  200. </entry>
  201. </row>
  202. <row>
  203. <entry>accountFilterFormat</entry>
  204. <entry>
  205. The LDAP search filter used to search for accounts. This string is a
  206. <ulink url="http://php.net/printf"><code>printf()</code></ulink> style expression that must
  207. contain one '<code>%s</code>' to accommodate the username. The default value is
  208. '<code>(&amp;(objectClass=user)(sAMAccountName=%s))</code>' unless
  209. <code>bindRequiresDn</code> is set to <constant>TRUE</constant>, in which case the default is
  210. '<code>(&amp;(objectClass=posixAccount)(uid=%s))</code>'. Users of custom schemas may need
  211. to change this option.
  212. </entry>
  213. </row>
  214. <row>
  215. <entry>allowEmptyPassword</entry>
  216. <entry>
  217. Some LDAP servers can be configured to accept an empty string
  218. password as an anonymous bind. This behavior is almost always
  219. undesirable. For this reason, empty passwords are explicitly
  220. disallowed. Set this value to <constant>TRUE</constant> to allow an
  221. empty string password to be submitted during the bind.
  222. </entry>
  223. </row>
  224. <row>
  225. <entry>optReferrals</entry>
  226. <entry>
  227. If set to <constant>TRUE</constant>, this option indicates to the LDAP client that referrals should
  228. be followed. The default value is <constant>FALSE</constant>.
  229. </entry>
  230. </row>
  231. </tbody>
  232. </tgroup>
  233. </table>
  234. </para>
  235. </sect3>
  236. <sect3 id="zend.ldap.using.theory-of-operation.account-name-canonicalization">
  237. <title>Account Name Canonicalization</title>
  238. <para>
  239. The <code>accountDomainName</code> and <code>accountDomainNameShort</code> options are used for two
  240. purposes: (1) they facilitate multi-domain authentication and failover capability, and (2) they are
  241. also used to canonicalize usernames. Specifically, names are canonicalized to the form specified by the
  242. <code>accountCanonicalForm</code> option. This option may one of the following values:
  243. <table id="zend.ldap.using.theory-of-operation.account-name-canonicalization.table">
  244. <title>accountCanonicalForm</title>
  245. <tgroup cols="3">
  246. <thead>
  247. <row>
  248. <entry>Name</entry>
  249. <entry>Value</entry>
  250. <entry>Example</entry>
  251. </row>
  252. </thead>
  253. <tbody>
  254. <row>
  255. <entry><code>ACCTNAME_FORM_DN</code></entry>
  256. <entry>1</entry>
  257. <entry>CN=Alice Baker,CN=Users,DC=example,DC=com</entry>
  258. </row>
  259. <row>
  260. <entry><code>ACCTNAME_FORM_USERNAME</code></entry>
  261. <entry>2</entry>
  262. <entry>abaker</entry>
  263. </row>
  264. <row>
  265. <entry><code>ACCTNAME_FORM_BACKSLASH</code></entry>
  266. <entry>3</entry>
  267. <entry>EXAMPLE\abaker</entry>
  268. </row>
  269. <row>
  270. <entry><code>ACCTNAME_FORM_PRINCIPAL</code></entry>
  271. <entry>4</entry>
  272. <entry>abaker@example.com</entry>
  273. </row>
  274. </tbody>
  275. </tgroup>
  276. </table>
  277. </para>
  278. <para>
  279. The default canonicalization depends on what account domain name options were supplied. If
  280. <code>accountDomainNameShort</code> was supplied, the default <code>accountCanonicalForm</code> value
  281. is <code>ACCTNAME_FORM_BACKSLASH</code>. Otherwise, if <code>accountDomainName</code> was supplied, the
  282. default is <code>ACCTNAME_FORM_PRINCIPAL</code>.
  283. </para>
  284. <para>
  285. Account name canonicalization ensures that the string used to identify an account is consistent
  286. regardless of what was supplied to <code>bind()</code>. For example, if the user supplies an account
  287. name of <emphasis>abaker@example.com</emphasis> or just <emphasis>abaker</emphasis> and the
  288. <code>accountCanonicalForm</code> is set to 3, the resulting canonicalized name would be
  289. <emphasis>EXAMPLE\abaker</emphasis>.
  290. </para>
  291. </sect3>
  292. <sect3 id="zend.ldap.using.theory-of-operation.multi-domain-failover">
  293. <title>Multi-domain Authentication and Failover</title>
  294. <para>
  295. The <classname>Zend_Ldap</classname> component by itself makes no attempt to authenticate with multiple servers.
  296. However, <classname>Zend_Ldap</classname> is specifically designed to handle this scenario gracefully. The
  297. required technique is to simply iterate over an array of arrays of server options and attempt to bind
  298. with each server. As described above <code>bind()</code> will automatically canonicalize each name, so
  299. it does not matter if the user passes <code>abaker@foo.net</code> or <code>W\bcarter</code> or
  300. <code>cdavis</code> - the <code>bind()</code> method will only succeed if the credentials were
  301. successfully used in the bind.
  302. </para>
  303. <para>
  304. Consider the following example that illustrates the technique required to implement multi-domain
  305. authentication and failover:
  306. <programlisting language="php"><![CDATA[
  307. $acctname = 'W\\user2';
  308. $password = 'pass2';
  309. $multiOptions = array(
  310. 'server1' => array(
  311. 'host' => 's0.foo.net',
  312. 'username' => 'CN=user1,DC=foo,DC=net',
  313. 'password' => 'pass1',
  314. 'bindRequiresDn' => true,
  315. 'accountDomainName' => 'foo.net',
  316. 'accountDomainNameShort' => 'FOO',
  317. 'accountCanonicalForm' => 4, // ACCT_FORM_PRINCIPAL
  318. 'baseDn' => 'OU=Sales,DC=foo,DC=net',
  319. ),
  320. 'server2' => array(
  321. 'host' => 'dc1.w.net',
  322. 'useSsl' => true,
  323. 'username' => 'user1@w.net',
  324. 'password' => 'pass1',
  325. 'accountDomainName' => 'w.net',
  326. 'accountDomainNameShort' => 'W',
  327. 'accountCanonicalForm' => 4, // ACCT_FORM_PRINCIPAL
  328. 'baseDn' => 'CN=Users,DC=w,DC=net',
  329. ),
  330. );
  331. $ldap = new Zend_Ldap();
  332. foreach ($multiOptions as $name => $options) {
  333. echo "Trying to bind using server options for '$name'\n";
  334. $ldap->setOptions($options);
  335. try {
  336. $ldap->bind($acctname, $password);
  337. $acctname = $ldap->getCanonicalAccountName($acctname);
  338. echo "SUCCESS: authenticated $acctname\n";
  339. return;
  340. } catch (Zend_Ldap_Exception $zle) {
  341. echo ' ' . $zle->getMessage() . "\n";
  342. if ($zle->getCode() === Zend_Ldap_Exception::LDAP_X_DOMAIN_MISMATCH) {
  343. continue;
  344. }
  345. }
  346. }
  347. ]]></programlisting>
  348. If the bind fails for any reason, the next set of server options is tried.
  349. </para>
  350. <para>
  351. The <code>getCanonicalAccountName</code> call gets the canonical account name that the application
  352. would presumably use to associate data with such as preferences. The
  353. <code>accountCanonicalForm = 4</code> in all server options ensures that the canonical form is
  354. consistent regardless of which server was ultimately used.
  355. </para>
  356. <para>
  357. The special <code>LDAP_X_DOMAIN_MISMATCH</code> exception occurs when an account name with a domain
  358. component was supplied (e.g., <code>abaker@foo.net</code> or <code>FOO\abaker</code> and not just
  359. <code>abaker</code>) but the domain component did not match either domain in the currently selected
  360. server options. This exception indicates that the server is not an authority for the account. In this
  361. case, the bind will not be performed, thereby eliminating unnecessary communication with the server.
  362. Note that the <code>continue</code> instruction has no effect in this example, but in practice for
  363. error handling and debugging purposes, you will probably want to check for
  364. <code>LDAP_X_DOMAIN_MISMATCH</code> as well as <code>LDAP_NO_SUCH_OBJECT</code> and
  365. <code>LDAP_INVALID_CREDENTIALS</code>.
  366. </para>
  367. <para>
  368. The above code is very similar to code used within
  369. <link linkend="zend.auth.adapter.ldap"><classname>Zend_Auth_Adapter_Ldap</classname></link>. In fact, we
  370. recommend that you simply use that authentication adapter for multi-domain + failover LDAP based
  371. authentication (or copy the code).
  372. </para>
  373. </sect3>
  374. </sect2>
  375. </sect1>
  376. <!--
  377. vim:se ts=4 sw=4 et:
  378. -->