BaseAuthenticate.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  13. */
  14. App::uses('Security', 'Utility');
  15. App::uses('Hash', 'Utility');
  16. /**
  17. * Base Authentication class with common methods and properties.
  18. *
  19. * @package Cake.Controller.Component.Auth
  20. */
  21. abstract class BaseAuthenticate {
  22. /**
  23. * Settings for this object.
  24. *
  25. * - `fields` The fields to use to identify a user by.
  26. * - `userModel` The model name of the User, defaults to User.
  27. * - `scope` Additional conditions to use when looking up and authenticating users,
  28. * i.e. `array('User.is_active' => 1).`
  29. * - `recursive` The value of the recursive key passed to find(). Defaults to 0.
  30. * - `contain` Extra models to contain and store in session.
  31. * - `passwordHasher` Password hasher class. Can be a string specifying class name
  32. * or an array containing `className` key, any other keys will be passed as
  33. * settings to the class. Defaults to 'Simple'.
  34. *
  35. * @var array
  36. */
  37. public $settings = array(
  38. 'fields' => array(
  39. 'username' => 'username',
  40. 'password' => 'password'
  41. ),
  42. 'userModel' => 'User',
  43. 'scope' => array(),
  44. 'recursive' => 0,
  45. 'contain' => null,
  46. 'passwordHasher' => 'Simple'
  47. );
  48. /**
  49. * A Component collection, used to get more components.
  50. *
  51. * @var ComponentCollection
  52. */
  53. protected $_Collection;
  54. /**
  55. * Password hasher instance.
  56. *
  57. * @var AbstractPasswordHasher
  58. */
  59. protected $_passwordHasher;
  60. /**
  61. * Constructor
  62. *
  63. * @param ComponentCollection $collection The Component collection used on this request.
  64. * @param array $settings Array of settings to use.
  65. */
  66. public function __construct(ComponentCollection $collection, $settings) {
  67. $this->_Collection = $collection;
  68. $this->settings = Hash::merge($this->settings, $settings);
  69. }
  70. /**
  71. * Find a user record using the standard options.
  72. *
  73. * The $username parameter can be a (string)username or an array containing
  74. * conditions for Model::find('first'). If the $password param is not provided
  75. * the password field will be present in returned array.
  76. *
  77. * Input passwords will be hashed even when a user doesn't exist. This
  78. * helps mitigate timing attacks that are attempting to find valid usernames.
  79. *
  80. * @param string|array $username The username/identifier, or an array of find conditions.
  81. * @param string $password The password, only used if $username param is string.
  82. * @return bool|array Either false on failure, or an array of user data.
  83. */
  84. protected function _findUser($username, $password = null) {
  85. $userModel = $this->settings['userModel'];
  86. list(, $model) = pluginSplit($userModel);
  87. $fields = $this->settings['fields'];
  88. if (is_array($username)) {
  89. $conditions = $username;
  90. } else {
  91. $conditions = array(
  92. $model . '.' . $fields['username'] => $username
  93. );
  94. }
  95. if (!empty($this->settings['scope'])) {
  96. $conditions = array_merge($conditions, $this->settings['scope']);
  97. }
  98. $result = ClassRegistry::init($userModel)->find('first', array(
  99. 'conditions' => $conditions,
  100. 'recursive' => $this->settings['recursive'],
  101. 'contain' => $this->settings['contain'],
  102. ));
  103. if (empty($result[$model])) {
  104. $this->passwordHasher()->hash($password);
  105. return false;
  106. }
  107. $user = $result[$model];
  108. if ($password !== null) {
  109. if (!$this->passwordHasher()->check($password, $user[$fields['password']])) {
  110. return false;
  111. }
  112. unset($user[$fields['password']]);
  113. }
  114. unset($result[$model]);
  115. return array_merge($user, $result);
  116. }
  117. /**
  118. * Return password hasher object
  119. *
  120. * @return AbstractPasswordHasher Password hasher instance
  121. * @throws CakeException If password hasher class not found or
  122. * it does not extend AbstractPasswordHasher
  123. */
  124. public function passwordHasher() {
  125. if ($this->_passwordHasher) {
  126. return $this->_passwordHasher;
  127. }
  128. $config = array();
  129. if (is_string($this->settings['passwordHasher'])) {
  130. $class = $this->settings['passwordHasher'];
  131. } else {
  132. $class = $this->settings['passwordHasher']['className'];
  133. $config = $this->settings['passwordHasher'];
  134. unset($config['className']);
  135. }
  136. list($plugin, $class) = pluginSplit($class, true);
  137. $className = $class . 'PasswordHasher';
  138. App::uses($className, $plugin . 'Controller/Component/Auth');
  139. if (!class_exists($className)) {
  140. throw new CakeException(__d('cake_dev', 'Password hasher class "%s" was not found.', $class));
  141. }
  142. if (!is_subclass_of($className, 'AbstractPasswordHasher')) {
  143. throw new CakeException(__d('cake_dev', 'Password hasher must extend AbstractPasswordHasher class.'));
  144. }
  145. $this->_passwordHasher = new $className($config);
  146. return $this->_passwordHasher;
  147. }
  148. /**
  149. * Hash the plain text password so that it matches the hashed/encrypted password
  150. * in the datasource.
  151. *
  152. * @param string $password The plain text password.
  153. * @return string The hashed form of the password.
  154. * @deprecated Since 2.4. Use a PasswordHasher class instead.
  155. */
  156. protected function _password($password) {
  157. return Security::hash($password, null, true);
  158. }
  159. /**
  160. * Authenticate a user based on the request information.
  161. *
  162. * @param CakeRequest $request Request to get authentication information from.
  163. * @param CakeResponse $response A response object that can have headers added.
  164. * @return mixed Either false on failure, or an array of user data on success.
  165. */
  166. abstract public function authenticate(CakeRequest $request, CakeResponse $response);
  167. /**
  168. * Allows you to hook into AuthComponent::logout(),
  169. * and implement specialized logout behavior.
  170. *
  171. * All attached authentication objects will have this method
  172. * called when a user logs out.
  173. *
  174. * @param array $user The user about to be logged out.
  175. * @return void
  176. */
  177. public function logout($user) {
  178. }
  179. /**
  180. * Get a user based on information in the request. Primarily used by stateless authentication
  181. * systems like basic and digest auth.
  182. *
  183. * @param CakeRequest $request Request object.
  184. * @return mixed Either false or an array of user information
  185. */
  186. public function getUser(CakeRequest $request) {
  187. return false;
  188. }
  189. /**
  190. * Handle unauthenticated access attempt.
  191. *
  192. * @param CakeRequest $request A request object.
  193. * @param CakeResponse $response A response object.
  194. * @return mixed Either true to indicate the unauthenticated request has been
  195. * dealt with and no more action is required by AuthComponent or void (default).
  196. */
  197. public function unauthenticated(CakeRequest $request, CakeResponse $response) {
  198. }
  199. }