Auth.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\security;
  9. use lithium\core\ConfigException;
  10. /**
  11. * The `Auth` class provides a common interface to authenticate user credentials from different
  12. * sources against different storage backends in a uniform way. As with most other adapter-driven
  13. * classes in the framework, `Auth` allows you to specify one or more named configurations,
  14. * including an adapter, which can be referenced by name in your application.
  15. *
  16. * `Auth` is responsible for managing session state for each configuration, and exposes a set of
  17. * methods which adapters can implement: `set()`, `check()` and `clear()`. You can read more about
  18. * each method below. Beyond these methods, `Auth` makes very few assumptions about how your
  19. * application authenticates users. Each adapter accepts a set of credentials, and returns an array
  20. * of user information on success, and `false` on failure. On successful authentication attempts,
  21. * the data returned from the credential check is written to the session, which is automatically
  22. * accessed on subsequent checks (though manual re-checking can be forced on a per-instance basis).
  23. *
  24. * To be secure by default (and if you don't override it), a `password` field is never stored in
  25. * the session adapter. This prevents a possible password hash to be leaked in a cookie (for
  26. * example). You can also be very specific on what you want to store in the session:
  27. *
  28. * {{{
  29. * Auth::config(array(
  30. * 'default' => array(
  31. * 'session' => array(
  32. * 'persist' => array('username', 'email')
  33. * )
  34. * )
  35. * ));
  36. * }}}
  37. *
  38. * You can also pass an optional `persist` param to the `check` method to override this default.
  39. *
  40. * For additional information on configuring and working with `Auth`, see the `Form` adapter.
  41. *
  42. * @see lithium\security\auth\adapter\Form
  43. */
  44. class Auth extends \lithium\core\Adaptable {
  45. /**
  46. * Stores configurations for various authentication adapters.
  47. *
  48. * @var object `Collection` of authentication configurations.
  49. */
  50. protected static $_configurations = array();
  51. /**
  52. * Libraries::locate() compatible path to adapters for this class.
  53. *
  54. * @see lithium\core\Libraries::locate()
  55. * @var string Dot-delimited path.
  56. */
  57. protected static $_adapters = 'adapter.security.auth';
  58. /**
  59. * Dynamic class dependencies.
  60. *
  61. * @var array Associative array of class names & their namespaces.
  62. */
  63. protected static $_classes = array(
  64. 'session' => 'lithium\storage\Session'
  65. );
  66. /**
  67. * Called when an adapter configuration is first accessed, this method sets the default
  68. * configuration for session handling. While each configuration can use its own session class
  69. * and options, this method initializes them to the default dependencies written into the class.
  70. * For the session key name, the default value is set to the name of the configuration.
  71. *
  72. * @param string $name The name of the adapter configuration being accessed.
  73. * @param array $config The user-specified configuration.
  74. * @return array Returns an array that merges the user-specified configuration with the
  75. * generated default values.
  76. */
  77. protected static function _initConfig($name, $config) {
  78. $defaults = array('session' => array(
  79. 'key' => $name,
  80. 'class' => static::$_classes['session'],
  81. 'options' => array(),
  82. 'persist' => array()
  83. ));
  84. $config = parent::_initConfig($name, $config) + $defaults;
  85. $config['session'] += $defaults['session'];
  86. return $config;
  87. }
  88. /**
  89. * Performs an authentication check against the specified configuration, and writes the
  90. * resulting user information to the session such that credentials are not required for
  91. * subsequent authentication checks, and user information is returned directly from the session.
  92. *
  93. * @param string $name The name of the `Auth` configuration/adapter to check against.
  94. * @param mixed $credentials A container for the authentication credentials used in this check.
  95. * This will vary by adapter, but generally will be an object or array containing
  96. * a user name and password. In the case of the `Form` adapter, it contains a
  97. * `Request` object containing `POST` data with user login information.
  98. * @param array $options Additional options used when performing the authentication check. The
  99. * options available will vary by adapter, please consult the documentation for the
  100. * `check()` method of the adapter you intend to use. The global options for this
  101. * method are:
  102. * - `'checkSession'` _boolean_: By default, the session store configured for the
  103. * adapter will always be queried first, to see if an authentication check has
  104. * already been performed during the current user session. If yes, then the
  105. * session data will be returned. By setting `'checkSession'` to `false`,
  106. * session checks are bypassed and the credentials provided are always checked
  107. * against the adapter directly.
  108. * - `'writeSession'` _boolean_: Upon a successful credentials check, the returned
  109. * user information is, by default, written to the session. Set this to `false`
  110. * to disable session writing for this authentication check.
  111. * - `'persist'` _array_: A list of fields that should be stored in the session.
  112. * @return array After a successful credential check against the adapter (or a successful
  113. * lookup against the current session), returns an array of user information from the
  114. * storage backend used by the configured adapter.
  115. * @filter
  116. */
  117. public static function check($name, $credentials = null, array $options = array()) {
  118. $config = static::config($name);
  119. $defaults = array(
  120. 'checkSession' => true,
  121. 'writeSession' => true,
  122. 'persist' => $config['session']['persist'] ?: static::_config('persist')
  123. );
  124. $options += $defaults;
  125. $params = compact('name', 'credentials', 'options');
  126. return static::_filter(__FUNCTION__, $params, function($self, $params) {
  127. extract($params);
  128. $config = $self::invokeMethod('_config', array($name));
  129. $persist = $options['persist'];
  130. if ($config === null) {
  131. throw new ConfigException("Configuration `{$name}` has not been defined.");
  132. }
  133. $session = $config['session'];
  134. if ($options['checkSession']) {
  135. if ($data = $session['class']::read($session['key'], $session['options'])) {
  136. return $data;
  137. }
  138. }
  139. if (($credentials) && $data = $self::adapter($name)->check($credentials, $options)) {
  140. if ($options['persist'] && is_array($data)) {
  141. $data = array_intersect_key($data, array_fill_keys($options['persist'], true));
  142. } elseif (is_array($data)) {
  143. unset($data['password']);
  144. }
  145. return ($options['writeSession']) ? $self::set($name, $data) : $data;
  146. }
  147. return false;
  148. });
  149. }
  150. /**
  151. * Manually authenticate a user with the given set of data. Rather than checking a user's
  152. * credentials, this method allows you to manually specify a user for whom you'd like to
  153. * initialize an authenticated session.
  154. *
  155. * By default, before writing the data to the session, the `set()` method of the named
  156. * configuration's adapter receives the data to be written, and has an opportunity to modify
  157. * or reject it.
  158. *
  159. * @param string $name The name of the adapter configuration to.
  160. * @param array $data The user data to be written to the session.
  161. * @param array $options Any additional session-writing options. These may override any options
  162. * set by the default session configuration for `$name`.
  163. * @return array Returns the array of data written to the session, or `false` if the adapter
  164. * rejects the data.
  165. * @filter
  166. */
  167. public static function set($name, $data, array $options = array()) {
  168. $params = compact('name', 'data', 'options');
  169. return static::_filter(__FUNCTION__, $params, function($self, $params) {
  170. extract($params);
  171. $config = $self::invokeMethod('_config', array($name));
  172. $session = $config['session'];
  173. if ($data = $self::adapter($name)->set($data, $options)) {
  174. $session['class']::write($session['key'], $data, $options + $session['options']);
  175. return $data;
  176. }
  177. return false;
  178. });
  179. }
  180. /**
  181. * Removes session information for the given configuration, and allows the configuration's
  182. * adapter to perform any associated cleanup tasks.
  183. *
  184. * @param string $name The name of the `Auth` configuration to clear the login information for.
  185. * Calls the `clear()` method of the given configuration's adapter, and removes
  186. * the information in the session key used by this configuration.
  187. * @param array $options Additional options used when clearing the authenticated session. See
  188. * each adapter's `clear()` method for all available options. Global options:
  189. * - `'clearSession'` _boolean_: If `true` (the default), session data for the
  190. * specified configuration is removed, otherwise it is retained.
  191. * @return void
  192. * @filter
  193. */
  194. public static function clear($name, array $options = array()) {
  195. $defaults = array('clearSession' => true);
  196. $options += $defaults;
  197. return static::_filter(__FUNCTION__, compact('name', 'options'), function($self, $params) {
  198. extract($params);
  199. $config = $self::invokeMethod('_config', array($name));
  200. $session = $config['session'];
  201. if ($options['clearSession']) {
  202. $session['class']::delete($session['key'], $session['options']);
  203. }
  204. $self::adapter($name)->clear($options);
  205. });
  206. }
  207. }
  208. ?>