## Apache Zeta Components Manual :: File Source for ldap_filter.php

## Source for file ldap_filter.php

Documentation is available at ldap_filter.php

1. <?php
2. /**
3. * File containing the ezcAuthenticationLdapFilter class.
4. *
5. * Licensed to the Apache Software Foundation (ASF) under one
6. * or more contributor license agreements. See the NOTICE file
7. * distributed with this work for additional information
8. * regarding copyright ownership. The ASF licenses this file
9. * to you under the Apache License, Version 2.0 (the
10. * "License"); you may not use this file except in compliance
11. * with the License. You may obtain a copy of the License at
12. *
13. * http://www.apache.org/licenses/LICENSE-2.0
14. *
15. * Unless required by applicable law or agreed to in writing,
16. * software distributed under the License is distributed on an
17. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18. * KIND, either express or implied. See the License for the
19. * specific language governing permissions and limitations
20. * under the License.
21. *
22. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
23. * @filesource
24. * @package Authentication
25. * @version //autogen//
26. */
28. /**
29. * Filter to authenticate against an LDAP directory.
30. *
31. * This filter depends on the PHP ldap extension. If this extension is not
32. * installed then the constructor will throw an ezcExtensionNotFoundException.
33. *
34. * RFC: {@link http://www.faqs.org/rfcs/rfc4510.html}
35. *
36. * Example:
37. * <code>
38. * $credentials = new ezcAuthenticationPasswordCredentials( 'jan.modaal', 'qwerty' );
39. * $ldap = new ezcAuthenticationLdapInfo( 'localhost', 'uid=%id%', 'dc=example,dc=com', 389 );
40. * $authentication = new ezcAuthentication( $credentials );
41. * $authentication->addFilter( new ezcAuthenticationLdapFilter( $ldap ) );
42. * // add more filters if needed
43. * if ( !$authentication->run() )
44. * {
45. * // authentication did not succeed, so inform the user
46. * $status = $authentication->getStatus();
47. * $err = array(
48. * 'ezcAuthenticationLdapFilter' => array(
49. * ezcAuthenticationLdapFilter::STATUS_USERNAME_INCORRECT => 'Incorrect username',
50. * ezcAuthenticationLdapFilter::STATUS_PASSWORD_INCORRECT => 'Incorrect password'
51. * )
52. * );
53. * foreach ( $status as $line )
54. * {
55. * list( $key, $value ) = each( $line );
56. * echo $err[$key][$value] . "\n";
57. * }
58. * }
59. * else
60. * {
61. * // authentication succeeded, so allow the user to see his content
62. * }
63. * </code>
64. *
65. * Extra data can be fetched from the LDAP server during the authentication
66. * process, by registering the data to be fetched before calling run(). Example:
67. * <code>
68. * // $filter is an ezcAuthenticationLdapFilter object
69. * $filter->registerFetchData( array( 'name', 'company', 'mobile' ) );
70. *
71. * // after run()
72. * $data = $filter->fetchData();
73. * </code>
74. *
75. * The $data array will be something like:
76. * <code>
77. * array( 'name' = > array( 'Dr. No' ),
78. * 'company' => array( 'SPECTRE' ),
79. * 'mobile' => array( '555-7732873' )
80. * );
81. * </code>
82. *
83. * @property ezcAuthenticationLdapInfo $ldap
84. * Structure which holds the LDAP server hostname, entry format and base,
85. * and port.
86. *
87. * @package Authentication
88. * @version //autogen//
89. * @mainclass
90. */
91. class ezcAuthenticationLdapFilter extends ezcAuthenticationFilter implements ezcAuthenticationDataFetch
92. {
93. /**
94. * Username is not found in the database.
95. */
96. const STATUS_USERNAME_INCORRECT = 1;
98. /**
99. * Password is incorrect.
100. */
101. const STATUS_PASSWORD_INCORRECT = 2;
103. /**
104. * Use plain-text password and no encryption for the connection (default).
105. */
106. const PROTOCOL_PLAIN = 1;
108. /**
109. * Use plain-text password and TLS connection.
110. */
111. const PROTOCOL_TLS = 2;
113. /**
114. * Holds the attributes which will be requested during the authentication
115. * process.
116. *
117. * Usually it has this structure:
118. * <code>
119. * array( 'name', 'company', 'mobile' );
120. * </code>
121. *
122. * @var array(string)
123. */
124. protected $requestedData = array();
126. /**
127. * Holds the extra data fetched during the authentication process.
128. *
129. * Usually it has this structure:
130. * <code>
131. * array( 'name' = > array( 'Dr. No' ),
132. * 'company' => array( 'SPECTRE' ),
133. * 'mobile' => array( '555-7732873' )
134. * );
135. * </code>
136. *
137. * @var array(string=>mixed)
138. */
139. protected $data = array();
141. /**
142. * Holds the properties of this class.
143. *
144. * @var array(string=>mixed)
145. */
146. private $properties = array();
148. /**
149. * Creates a new object of this class.
150. *
151. * @throws ezcBaseExtensionNotFoundException
152. * if the PHP ldap extension is not installed
153. * @param ezcAuthenticationLdapInfo $ldap How to connect to LDAP
154. * @param ezcAuthenticationLdapOptions $options Options for this class
155. */
156. public function __construct ( ezcAuthenticationLdapInfo $ldap, ezcAuthenticationLdapOptions $options = null )
157. {
158. if ( ! ezcBaseFeatures :: hasExtensionSupport ( 'ldap' ) )
159. {
160. throw new ezcBaseExtensionNotFoundException ( 'ldap', null, "PHP not configured with --with-ldap." );
161. }
163. $this->ldap = $ldap;
164. $this-> options = ( $options === null ) ? new ezcAuthenticationLdapOptions () : $options;
165. }
167. /**
168. * Sets the property $name to $value.
169. *
170. * @throws ezcBasePropertyNotFoundException
171. * if the property $name does not exist
172. * @throws ezcBaseValueException
173. * if $value is not correct for the property $name
174. * @param string $name The name of the property to set
175. * @param mixed $value The new value of the property
176. * @ignore
177. */
178. public function __set( $name, $value )
179. {
180. switch ( $name )
181. {
182. case 'ldap':
183. if ( $value instanceof ezcAuthenticationLdapInfo )
184. {
185. $this->properties[$name] = $value;
186. }
187. else
188. {
189. throw new ezcBaseValueException ( $name, $value, 'ezcAuthenticationLdapInfo' );
190. }
191. break;
193. default:
194. throw new ezcBasePropertyNotFoundException ( $name );
195. }
196. }
198. /**
199. * Returns the value of the property $name.
200. *
201. * @throws ezcBasePropertyNotFoundException
202. * if the property $name does not exist
203. * @param string $name The name of the property for which to return the value
204. * @return mixed
205. * @ignore
206. */
207. public function __get( $name )
208. {
209. switch ( $name )
210. {
211. case 'ldap':
212. return $this->properties[$name];
214. default:
215. throw new ezcBasePropertyNotFoundException ( $name );
216. }
217. }
219. /**
220. * Returns true if the property $name is set, otherwise false.
221. *
222. * @param string $name The name of the property to test if it is set
223. * @return bool
224. * @ignore
225. */
226. public function __isset( $name )
227. {
228. switch ( $name )
229. {
230. case 'ldap':
231. return isset( $this->properties[$name] );
233. default:
234. return false;
235. }
236. }
238. /**
239. * Runs the filter and returns a status code when finished.
240. *
241. * @throws ezcAuthenticationLdapException
242. * if the connecting and binding to the LDAP server could not be performed
243. * @param ezcAuthenticationPasswordCredentials $credentials Authentication credentials
244. * @return int
245. */
246. public function run ( $credentials )
247. {
248. $protocol = 'ldap://'; // 'ldaps://' will be implemented later (if ever, as TLS is better)
250. // null, false or empty string passwords are not accepted, as on some servers
251. // they could cause the LDAP binding to succeed
252. if ( empty( $credentials->password ) )
253. {
254. return self::STATUS_PASSWORD_INCORRECT;
255. }
257. $connection = $this-> ldapConnect ( $this->ldap->host, $this->ldap->port );
258. if ( !$connection )
259. {
260. // OpenLDAP 2.x.x will not throw an exception because $connection is always a resource
261. throw new ezcAuthenticationLdapException ( "Could not connect to host '{$protocol}{$this->ldap->host}:{$this

->ldap->port}'." );
- }
- // without using version 3, TLS and other stuff won't work
- ldap_set_option( $connection, LDAP_OPT_PROTOCOL_VERSION, 3 );
- ldap_set_option( $connection, LDAP_OPT_REFERRALS, 0 );
- // try to use a TLS connection
- if ( $this-> options ->protocol === self::PROTOCOL_TLS || $this->ldap->protocol === self::PROTOCOL_TLS )
- {
- if ( $this-> ldapStartTls ( $connection ) )
- {
- // using TLS, so continue
- }
- else
- {
- throw new ezcAuthenticationLdapException ( "Could not connect to host '{$protocol}{$this->ldap->host}:{$this->ldap->port}'." );
- }
- }
- // bind anonymously to see if username exists in the directory
- if ( @ldap_bind( $connection ) )
- {
- $search = @ldap_search( $connection, $this->ldap->base, str_replace ( '%id%', $credentials->id, $this->ldap->format ), $this-> requestedData );
- if ( !$search || ldap_count_entries( $connection, $search ) === 0 )
- {
- ldap_close( $connection );
- return self::STATUS_USERNAME_INCORRECT;
- }
- // username exists, so get dn for it
- $entry = ldap_first_entry( $connection, $search );
- $entryDN = ldap_get_dn( $connection, $entry );
- // check if we can bind with the provided password
- if ( @ldap_bind( $connection, $entryDN, $credentials->password ) )
- {
- // retrieve extra authentication data
- if ( count ( $this-> requestedData ) > 0 )
- {
- $attributes = ldap_get_attributes( $connection, $entry );
- foreach ( $this-> requestedData as $attribute )
- {
- // ignore case of $attribute
- if ( isset( $attributes[$attribute] )
- || isset( $attributes[ strtolower ( $attribute )] )
- )
- {
- for ( $i = 0; $i < $attributes[$attribute]['count']; $i++ )
- {
- $this-> data [$attribute][] = $attributes[$attribute][$i];
- }
- }
- // DN is a 'special' attribute and is not returned by ldap_get_attributes()
- if ( strtolower ( $attribute ) == 'dn' )
- {
- // An entry can only have one DN
- $this-> data [$attribute] = $entryDN;
- }
- }
- }
- ldap_close( $connection );
- return self::STATUS_OK;
- }
- }
- // bind failed, so something must be wrong (connection error or password incorrect)
- $err = ldap_errno( $connection );
- ldap_close( $connection );
- // error codes: 0 = success, 49 = invalidCredentials, 50 = insufficientAccessRights
- // so if any other codes appear it must mean that we could not connect to
- // the LDAP host
- if ( $err !== 0 && $err !== 49 && $err !== 50 )
- {
- throw new ezcAuthenticationLdapException ( "Could not connect to host '{$protocol}{$this->ldap->host}:{$this->ldap->port}'", $err, ldap_err2str( $err ) );
- }
- return self::STATUS_PASSWORD_INCORRECT;
- }
- /**
- * Wraps around the ldap_connect() function.
- *
- * Returns the connection as a resource if it was successful.
- *
- * @param string $host The LDAP hostname
- * @param int $port The LDAP port to connect to $host, default 389
- * @return mixed
- */
- protected function ldapConnect ( $host, $port = 389 )
- {
- return ldap_connect( $host, $port );
- }
- /**
- * Wraps around the ldap_start_tls() function.
- *
- * Returns true if it was possible to start a TLS connection on the provided
- * $connection.
- *
- * @param mixed $connection An established LDAP connection
- * @return bool
- */
- protected function ldapStartTls ( $connection )
- {
- return @ldap_start_tls( $connection );
- }
- /**
- * Registers which extra data to fetch during the authentication process.
- *
- * The input $data is an array of attributes to request, for example:
- * <code>
- * array( 'name', 'company', 'mobile' );
- * </code>
- *
- * @param array(string) $data A list of attributes to fetch during authentication
- */
- public function registerFetchData ( array $data = array() )
- {
- $this-> requestedData = $data;
- }
- /**
- * Returns the extra data fetched during the authentication process.
- *
- * The return is something like:
- * <code>
- * array( 'name' = > array( 'Dr. No' ),
- * 'company' => array( 'SPECTRE' ),
- * 'mobile' => array( '555-7732873' )
- * );
- * </code>
- *
- * @return array(string=>mixed)
- */
- public function fetchData ()
- {
- return $this-> data ;
- }
- }
- ?>
Documentation generated by phpDocumentor 1.4.3