|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Auth |
|
17 * @subpackage Adapter |
|
18 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
19 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
20 * @version $Id: DbTable.php 22613 2010-07-17 13:43:22Z dragonbe $ |
|
21 */ |
|
22 |
|
23 |
|
24 /** |
|
25 * @see Zend_Auth_Adapter_Interface |
|
26 */ |
|
27 require_once 'Zend/Auth/Adapter/Interface.php'; |
|
28 |
|
29 /** |
|
30 * @see Zend_Db_Adapter_Abstract |
|
31 */ |
|
32 require_once 'Zend/Db/Adapter/Abstract.php'; |
|
33 |
|
34 /** |
|
35 * @see Zend_Auth_Result |
|
36 */ |
|
37 require_once 'Zend/Auth/Result.php'; |
|
38 |
|
39 |
|
40 /** |
|
41 * @category Zend |
|
42 * @package Zend_Auth |
|
43 * @subpackage Adapter |
|
44 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
45 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
46 */ |
|
47 class Zend_Auth_Adapter_DbTable implements Zend_Auth_Adapter_Interface |
|
48 { |
|
49 |
|
50 /** |
|
51 * Database Connection |
|
52 * |
|
53 * @var Zend_Db_Adapter_Abstract |
|
54 */ |
|
55 protected $_zendDb = null; |
|
56 |
|
57 /** |
|
58 * @var Zend_Db_Select |
|
59 */ |
|
60 protected $_dbSelect = null; |
|
61 |
|
62 /** |
|
63 * $_tableName - the table name to check |
|
64 * |
|
65 * @var string |
|
66 */ |
|
67 protected $_tableName = null; |
|
68 |
|
69 /** |
|
70 * $_identityColumn - the column to use as the identity |
|
71 * |
|
72 * @var string |
|
73 */ |
|
74 protected $_identityColumn = null; |
|
75 |
|
76 /** |
|
77 * $_credentialColumns - columns to be used as the credentials |
|
78 * |
|
79 * @var string |
|
80 */ |
|
81 protected $_credentialColumn = null; |
|
82 |
|
83 /** |
|
84 * $_identity - Identity value |
|
85 * |
|
86 * @var string |
|
87 */ |
|
88 protected $_identity = null; |
|
89 |
|
90 /** |
|
91 * $_credential - Credential values |
|
92 * |
|
93 * @var string |
|
94 */ |
|
95 protected $_credential = null; |
|
96 |
|
97 /** |
|
98 * $_credentialTreatment - Treatment applied to the credential, such as MD5() or PASSWORD() |
|
99 * |
|
100 * @var string |
|
101 */ |
|
102 protected $_credentialTreatment = null; |
|
103 |
|
104 /** |
|
105 * $_authenticateResultInfo |
|
106 * |
|
107 * @var array |
|
108 */ |
|
109 protected $_authenticateResultInfo = null; |
|
110 |
|
111 /** |
|
112 * $_resultRow - Results of database authentication query |
|
113 * |
|
114 * @var array |
|
115 */ |
|
116 protected $_resultRow = null; |
|
117 |
|
118 /** |
|
119 * $_ambiguityIdentity - Flag to indicate same Identity can be used with |
|
120 * different credentials. Default is FALSE and need to be set to true to |
|
121 * allow ambiguity usage. |
|
122 * |
|
123 * @var boolean |
|
124 */ |
|
125 protected $_ambiguityIdentity = false; |
|
126 |
|
127 /** |
|
128 * __construct() - Sets configuration options |
|
129 * |
|
130 * @param Zend_Db_Adapter_Abstract $zendDb If null, default database adapter assumed |
|
131 * @param string $tableName |
|
132 * @param string $identityColumn |
|
133 * @param string $credentialColumn |
|
134 * @param string $credentialTreatment |
|
135 * @return void |
|
136 */ |
|
137 public function __construct(Zend_Db_Adapter_Abstract $zendDb = null, $tableName = null, $identityColumn = null, |
|
138 $credentialColumn = null, $credentialTreatment = null) |
|
139 { |
|
140 $this->_setDbAdapter($zendDb); |
|
141 |
|
142 if (null !== $tableName) { |
|
143 $this->setTableName($tableName); |
|
144 } |
|
145 |
|
146 if (null !== $identityColumn) { |
|
147 $this->setIdentityColumn($identityColumn); |
|
148 } |
|
149 |
|
150 if (null !== $credentialColumn) { |
|
151 $this->setCredentialColumn($credentialColumn); |
|
152 } |
|
153 |
|
154 if (null !== $credentialTreatment) { |
|
155 $this->setCredentialTreatment($credentialTreatment); |
|
156 } |
|
157 } |
|
158 |
|
159 /** |
|
160 * _setDbAdapter() - set the database adapter to be used for quering |
|
161 * |
|
162 * @param Zend_Db_Adapter_Abstract |
|
163 * @throws Zend_Auth_Adapter_Exception |
|
164 * @return Zend_Auth_Adapter_DbTable |
|
165 */ |
|
166 protected function _setDbAdapter(Zend_Db_Adapter_Abstract $zendDb = null) |
|
167 { |
|
168 $this->_zendDb = $zendDb; |
|
169 |
|
170 /** |
|
171 * If no adapter is specified, fetch default database adapter. |
|
172 */ |
|
173 if(null === $this->_zendDb) { |
|
174 require_once 'Zend/Db/Table/Abstract.php'; |
|
175 $this->_zendDb = Zend_Db_Table_Abstract::getDefaultAdapter(); |
|
176 if (null === $this->_zendDb) { |
|
177 require_once 'Zend/Auth/Adapter/Exception.php'; |
|
178 throw new Zend_Auth_Adapter_Exception('No database adapter present'); |
|
179 } |
|
180 } |
|
181 |
|
182 return $this; |
|
183 } |
|
184 |
|
185 /** |
|
186 * setTableName() - set the table name to be used in the select query |
|
187 * |
|
188 * @param string $tableName |
|
189 * @return Zend_Auth_Adapter_DbTable Provides a fluent interface |
|
190 */ |
|
191 public function setTableName($tableName) |
|
192 { |
|
193 $this->_tableName = $tableName; |
|
194 return $this; |
|
195 } |
|
196 |
|
197 /** |
|
198 * setIdentityColumn() - set the column name to be used as the identity column |
|
199 * |
|
200 * @param string $identityColumn |
|
201 * @return Zend_Auth_Adapter_DbTable Provides a fluent interface |
|
202 */ |
|
203 public function setIdentityColumn($identityColumn) |
|
204 { |
|
205 $this->_identityColumn = $identityColumn; |
|
206 return $this; |
|
207 } |
|
208 |
|
209 /** |
|
210 * setCredentialColumn() - set the column name to be used as the credential column |
|
211 * |
|
212 * @param string $credentialColumn |
|
213 * @return Zend_Auth_Adapter_DbTable Provides a fluent interface |
|
214 */ |
|
215 public function setCredentialColumn($credentialColumn) |
|
216 { |
|
217 $this->_credentialColumn = $credentialColumn; |
|
218 return $this; |
|
219 } |
|
220 |
|
221 /** |
|
222 * setCredentialTreatment() - allows the developer to pass a parameterized string that is |
|
223 * used to transform or treat the input credential data. |
|
224 * |
|
225 * In many cases, passwords and other sensitive data are encrypted, hashed, encoded, |
|
226 * obscured, or otherwise treated through some function or algorithm. By specifying a |
|
227 * parameterized treatment string with this method, a developer may apply arbitrary SQL |
|
228 * upon input credential data. |
|
229 * |
|
230 * Examples: |
|
231 * |
|
232 * 'PASSWORD(?)' |
|
233 * 'MD5(?)' |
|
234 * |
|
235 * @param string $treatment |
|
236 * @return Zend_Auth_Adapter_DbTable Provides a fluent interface |
|
237 */ |
|
238 public function setCredentialTreatment($treatment) |
|
239 { |
|
240 $this->_credentialTreatment = $treatment; |
|
241 return $this; |
|
242 } |
|
243 |
|
244 /** |
|
245 * setIdentity() - set the value to be used as the identity |
|
246 * |
|
247 * @param string $value |
|
248 * @return Zend_Auth_Adapter_DbTable Provides a fluent interface |
|
249 */ |
|
250 public function setIdentity($value) |
|
251 { |
|
252 $this->_identity = $value; |
|
253 return $this; |
|
254 } |
|
255 |
|
256 /** |
|
257 * setCredential() - set the credential value to be used, optionally can specify a treatment |
|
258 * to be used, should be supplied in parameterized form, such as 'MD5(?)' or 'PASSWORD(?)' |
|
259 * |
|
260 * @param string $credential |
|
261 * @return Zend_Auth_Adapter_DbTable Provides a fluent interface |
|
262 */ |
|
263 public function setCredential($credential) |
|
264 { |
|
265 $this->_credential = $credential; |
|
266 return $this; |
|
267 } |
|
268 |
|
269 /** |
|
270 * setAmbiguityIdentity() - sets a flag for usage of identical identities |
|
271 * with unique credentials. It accepts integers (0, 1) or boolean (true, |
|
272 * false) parameters. Default is false. |
|
273 * |
|
274 * @param int|bool $flag |
|
275 * @return Zend_Auth_Adapter_DbTable |
|
276 */ |
|
277 public function setAmbiguityIdentity($flag) |
|
278 { |
|
279 if (is_integer($flag)) { |
|
280 $this->_ambiguityIdentity = (1 === $flag ? true : false); |
|
281 } elseif (is_bool($flag)) { |
|
282 $this->_ambiguityIdentity = $flag; |
|
283 } |
|
284 return $this; |
|
285 } |
|
286 /** |
|
287 * getAmbiguityIdentity() - returns TRUE for usage of multiple identical |
|
288 * identies with different credentials, FALSE if not used. |
|
289 * |
|
290 * @return bool |
|
291 */ |
|
292 public function getAmbiguityIdentity() |
|
293 { |
|
294 return $this->_ambiguityIdentity; |
|
295 } |
|
296 |
|
297 /** |
|
298 * getDbSelect() - Return the preauthentication Db Select object for userland select query modification |
|
299 * |
|
300 * @return Zend_Db_Select |
|
301 */ |
|
302 public function getDbSelect() |
|
303 { |
|
304 if ($this->_dbSelect == null) { |
|
305 $this->_dbSelect = $this->_zendDb->select(); |
|
306 } |
|
307 |
|
308 return $this->_dbSelect; |
|
309 } |
|
310 |
|
311 /** |
|
312 * getResultRowObject() - Returns the result row as a stdClass object |
|
313 * |
|
314 * @param string|array $returnColumns |
|
315 * @param string|array $omitColumns |
|
316 * @return stdClass|boolean |
|
317 */ |
|
318 public function getResultRowObject($returnColumns = null, $omitColumns = null) |
|
319 { |
|
320 if (!$this->_resultRow) { |
|
321 return false; |
|
322 } |
|
323 |
|
324 $returnObject = new stdClass(); |
|
325 |
|
326 if (null !== $returnColumns) { |
|
327 |
|
328 $availableColumns = array_keys($this->_resultRow); |
|
329 foreach ( (array) $returnColumns as $returnColumn) { |
|
330 if (in_array($returnColumn, $availableColumns)) { |
|
331 $returnObject->{$returnColumn} = $this->_resultRow[$returnColumn]; |
|
332 } |
|
333 } |
|
334 return $returnObject; |
|
335 |
|
336 } elseif (null !== $omitColumns) { |
|
337 |
|
338 $omitColumns = (array) $omitColumns; |
|
339 foreach ($this->_resultRow as $resultColumn => $resultValue) { |
|
340 if (!in_array($resultColumn, $omitColumns)) { |
|
341 $returnObject->{$resultColumn} = $resultValue; |
|
342 } |
|
343 } |
|
344 return $returnObject; |
|
345 |
|
346 } else { |
|
347 |
|
348 foreach ($this->_resultRow as $resultColumn => $resultValue) { |
|
349 $returnObject->{$resultColumn} = $resultValue; |
|
350 } |
|
351 return $returnObject; |
|
352 |
|
353 } |
|
354 } |
|
355 |
|
356 /** |
|
357 * authenticate() - defined by Zend_Auth_Adapter_Interface. This method is called to |
|
358 * attempt an authentication. Previous to this call, this adapter would have already |
|
359 * been configured with all necessary information to successfully connect to a database |
|
360 * table and attempt to find a record matching the provided identity. |
|
361 * |
|
362 * @throws Zend_Auth_Adapter_Exception if answering the authentication query is impossible |
|
363 * @return Zend_Auth_Result |
|
364 */ |
|
365 public function authenticate() |
|
366 { |
|
367 $this->_authenticateSetup(); |
|
368 $dbSelect = $this->_authenticateCreateSelect(); |
|
369 $resultIdentities = $this->_authenticateQuerySelect($dbSelect); |
|
370 |
|
371 if ( ($authResult = $this->_authenticateValidateResultSet($resultIdentities)) instanceof Zend_Auth_Result) { |
|
372 return $authResult; |
|
373 } |
|
374 |
|
375 if (true === $this->getAmbiguityIdentity()) { |
|
376 $validIdentities = array (); |
|
377 $zendAuthCredentialMatchColumn = $this->_zendDb->foldCase('zend_auth_credential_match'); |
|
378 foreach ($resultIdentities as $identity) { |
|
379 if (1 === (int) $identity[$zendAuthCredentialMatchColumn]) { |
|
380 $validIdentities[] = $identity; |
|
381 } |
|
382 } |
|
383 $resultIdentities = $validIdentities; |
|
384 } |
|
385 |
|
386 $authResult = $this->_authenticateValidateResult(array_shift($resultIdentities)); |
|
387 return $authResult; |
|
388 } |
|
389 |
|
390 /** |
|
391 * _authenticateSetup() - This method abstracts the steps involved with |
|
392 * making sure that this adapter was indeed setup properly with all |
|
393 * required pieces of information. |
|
394 * |
|
395 * @throws Zend_Auth_Adapter_Exception - in the event that setup was not done properly |
|
396 * @return true |
|
397 */ |
|
398 protected function _authenticateSetup() |
|
399 { |
|
400 $exception = null; |
|
401 |
|
402 if ($this->_tableName == '') { |
|
403 $exception = 'A table must be supplied for the Zend_Auth_Adapter_DbTable authentication adapter.'; |
|
404 } elseif ($this->_identityColumn == '') { |
|
405 $exception = 'An identity column must be supplied for the Zend_Auth_Adapter_DbTable authentication adapter.'; |
|
406 } elseif ($this->_credentialColumn == '') { |
|
407 $exception = 'A credential column must be supplied for the Zend_Auth_Adapter_DbTable authentication adapter.'; |
|
408 } elseif ($this->_identity == '') { |
|
409 $exception = 'A value for the identity was not provided prior to authentication with Zend_Auth_Adapter_DbTable.'; |
|
410 } elseif ($this->_credential === null) { |
|
411 $exception = 'A credential value was not provided prior to authentication with Zend_Auth_Adapter_DbTable.'; |
|
412 } |
|
413 |
|
414 if (null !== $exception) { |
|
415 /** |
|
416 * @see Zend_Auth_Adapter_Exception |
|
417 */ |
|
418 require_once 'Zend/Auth/Adapter/Exception.php'; |
|
419 throw new Zend_Auth_Adapter_Exception($exception); |
|
420 } |
|
421 |
|
422 $this->_authenticateResultInfo = array( |
|
423 'code' => Zend_Auth_Result::FAILURE, |
|
424 'identity' => $this->_identity, |
|
425 'messages' => array() |
|
426 ); |
|
427 |
|
428 return true; |
|
429 } |
|
430 |
|
431 /** |
|
432 * _authenticateCreateSelect() - This method creates a Zend_Db_Select object that |
|
433 * is completely configured to be queried against the database. |
|
434 * |
|
435 * @return Zend_Db_Select |
|
436 */ |
|
437 protected function _authenticateCreateSelect() |
|
438 { |
|
439 // build credential expression |
|
440 if (empty($this->_credentialTreatment) || (strpos($this->_credentialTreatment, '?') === false)) { |
|
441 $this->_credentialTreatment = '?'; |
|
442 } |
|
443 |
|
444 $credentialExpression = new Zend_Db_Expr( |
|
445 '(CASE WHEN ' . |
|
446 $this->_zendDb->quoteInto( |
|
447 $this->_zendDb->quoteIdentifier($this->_credentialColumn, true) |
|
448 . ' = ' . $this->_credentialTreatment, $this->_credential |
|
449 ) |
|
450 . ' THEN 1 ELSE 0 END) AS ' |
|
451 . $this->_zendDb->quoteIdentifier( |
|
452 $this->_zendDb->foldCase('zend_auth_credential_match') |
|
453 ) |
|
454 ); |
|
455 |
|
456 // get select |
|
457 $dbSelect = clone $this->getDbSelect(); |
|
458 $dbSelect->from($this->_tableName, array('*', $credentialExpression)) |
|
459 ->where($this->_zendDb->quoteIdentifier($this->_identityColumn, true) . ' = ?', $this->_identity); |
|
460 |
|
461 return $dbSelect; |
|
462 } |
|
463 |
|
464 /** |
|
465 * _authenticateQuerySelect() - This method accepts a Zend_Db_Select object and |
|
466 * performs a query against the database with that object. |
|
467 * |
|
468 * @param Zend_Db_Select $dbSelect |
|
469 * @throws Zend_Auth_Adapter_Exception - when an invalid select |
|
470 * object is encountered |
|
471 * @return array |
|
472 */ |
|
473 protected function _authenticateQuerySelect(Zend_Db_Select $dbSelect) |
|
474 { |
|
475 try { |
|
476 if ($this->_zendDb->getFetchMode() != Zend_DB::FETCH_ASSOC) { |
|
477 $origDbFetchMode = $this->_zendDb->getFetchMode(); |
|
478 $this->_zendDb->setFetchMode(Zend_DB::FETCH_ASSOC); |
|
479 } |
|
480 $resultIdentities = $this->_zendDb->fetchAll($dbSelect->__toString()); |
|
481 if (isset($origDbFetchMode)) { |
|
482 $this->_zendDb->setFetchMode($origDbFetchMode); |
|
483 unset($origDbFetchMode); |
|
484 } |
|
485 } catch (Exception $e) { |
|
486 /** |
|
487 * @see Zend_Auth_Adapter_Exception |
|
488 */ |
|
489 require_once 'Zend/Auth/Adapter/Exception.php'; |
|
490 throw new Zend_Auth_Adapter_Exception('The supplied parameters to Zend_Auth_Adapter_DbTable failed to ' |
|
491 . 'produce a valid sql statement, please check table and column names ' |
|
492 . 'for validity.', 0, $e); |
|
493 } |
|
494 return $resultIdentities; |
|
495 } |
|
496 |
|
497 /** |
|
498 * _authenticateValidateResultSet() - This method attempts to make |
|
499 * certain that only one record was returned in the resultset |
|
500 * |
|
501 * @param array $resultIdentities |
|
502 * @return true|Zend_Auth_Result |
|
503 */ |
|
504 protected function _authenticateValidateResultSet(array $resultIdentities) |
|
505 { |
|
506 |
|
507 if (count($resultIdentities) < 1) { |
|
508 $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND; |
|
509 $this->_authenticateResultInfo['messages'][] = 'A record with the supplied identity could not be found.'; |
|
510 return $this->_authenticateCreateAuthResult(); |
|
511 } elseif (count($resultIdentities) > 1 && false === $this->getAmbiguityIdentity()) { |
|
512 $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS; |
|
513 $this->_authenticateResultInfo['messages'][] = 'More than one record matches the supplied identity.'; |
|
514 return $this->_authenticateCreateAuthResult(); |
|
515 } |
|
516 |
|
517 return true; |
|
518 } |
|
519 |
|
520 /** |
|
521 * _authenticateValidateResult() - This method attempts to validate that |
|
522 * the record in the resultset is indeed a record that matched the |
|
523 * identity provided to this adapter. |
|
524 * |
|
525 * @param array $resultIdentity |
|
526 * @return Zend_Auth_Result |
|
527 */ |
|
528 protected function _authenticateValidateResult($resultIdentity) |
|
529 { |
|
530 $zendAuthCredentialMatchColumn = $this->_zendDb->foldCase('zend_auth_credential_match'); |
|
531 |
|
532 if ($resultIdentity[$zendAuthCredentialMatchColumn] != '1') { |
|
533 $this->_authenticateResultInfo['code'] = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID; |
|
534 $this->_authenticateResultInfo['messages'][] = 'Supplied credential is invalid.'; |
|
535 return $this->_authenticateCreateAuthResult(); |
|
536 } |
|
537 |
|
538 unset($resultIdentity[$zendAuthCredentialMatchColumn]); |
|
539 $this->_resultRow = $resultIdentity; |
|
540 |
|
541 $this->_authenticateResultInfo['code'] = Zend_Auth_Result::SUCCESS; |
|
542 $this->_authenticateResultInfo['messages'][] = 'Authentication successful.'; |
|
543 return $this->_authenticateCreateAuthResult(); |
|
544 } |
|
545 |
|
546 /** |
|
547 * _authenticateCreateAuthResult() - Creates a Zend_Auth_Result object from |
|
548 * the information that has been collected during the authenticate() attempt. |
|
549 * |
|
550 * @return Zend_Auth_Result |
|
551 */ |
|
552 protected function _authenticateCreateAuthResult() |
|
553 { |
|
554 return new Zend_Auth_Result( |
|
555 $this->_authenticateResultInfo['code'], |
|
556 $this->_authenticateResultInfo['identity'], |
|
557 $this->_authenticateResultInfo['messages'] |
|
558 ); |
|
559 } |
|
560 |
|
561 } |