Phly Documentation Phly
[ return to channel ] [ class tree: Phly ] [ index: Phly ] [ all elements ]

Source for file Auth.php

Documentation is available at Auth.php

  1. <?php
  2. /**
  3. * PHLY - PHp LibrarY
  4. *
  5. * PHLY is a library of PHP classes designed with the following intentions:
  6. * - Loosely coupled; dependencies should be few, and no base class should be
  7. * necessary.
  8. * - Extendible; all classes should be easily extendible. This may be via
  9. * observers, interfaces, adapters, etc.. The base class should solve 80% of
  10. * usage, and allow extensions to the class to fill in the remainder.
  11. * - Designed for PHP5 and up; all classes should make use of PHP5's features.
  12. * - Documented; all classes should minimally have excellent API-level
  13. * documentation, with use cases in the class docblock.
  14. * - Tested; all classes should have (passing) unit tests accompanying them.
  15. * - Open source and commercial friendly; all classes should use a
  16. * commercial-friendly open source license. The BSD license is one such
  17. * example.
  18. *
  19. * @license New BSD, http://www.opensource.org/licenses/bsd-license.php
  20. * @package Phly
  21. * @copyright 2006 - Present, Matthew Weier O'Phinney
  22. */
  23.  
  24. /**
  25. * Exceptions
  26. */
  27. require_once 'Phly/Auth/Exception.php';
  28.  
  29. /**
  30. * Authentication class
  31. *
  32. * Pluggable authentication class. Credentials are checked against the provided
  33. * callback (passed to the constructor), and valid users then receive an
  34. * authentication session. The authentication session includes the following
  35. * keys:
  36. * - authenticated; always set to true
  37. * - username; set to the username provided at login, unless the validation
  38. * callback returns a non boolean true value, in which case that value will be
  39. * used.
  40. * - loginTime; timestamp of initial login
  41. * - timeStamp; timestamp of most recent page visit
  42. * - userData; a second argument to the constructor may be passed, a callback to
  43. * retrieve user data. If so provided, any return value is stored in this key.
  44. * This might contain ACL permissions, additional demographic information, etc
  45. * - sessionData; any extra session data you wish to associate with the
  46. * authentication session.
  47. *
  48. * The above properties, and all properties in the sessionData array, may be
  49. * accessed as object properties. (Note: make sure your sessionData keys don't
  50. * collide with those in the main auth array.)
  51. *
  52. * You may also configure the authentication class. Simply use the
  53. * {@link config()} method, setting one of the {@link $_config} keys as desired;
  54. * see {@link $_config} for some sample keys and default values.
  55. *
  56. * Examples:
  57. * <code>
  58. * class MyAuth
  59. * {
  60. * public static function isValid($username, $password)
  61. * {
  62. * $users = parse_ini_file('./users.ini', true);
  63. * if (isset($users[$username])
  64. * && (md5($password) == $users[$username]['password']))
  65. * {
  66. * return true;
  67. * }
  68. *
  69. * return false;
  70. * }
  71. *
  72. * public static function getUserData($username)
  73. * {
  74. * $users = parse_ini_file('./users.ini', true);
  75. * if (isset($users[$username])) {
  76. * $user = $users[$username];
  77. * unset($user['password']);
  78. * return $user;
  79. * }
  80. *
  81. * return array();
  82. * }
  83. * }
  84. *
  85. * require_once 'Phly/Auth.php';
  86. * $auth = new Phly_Auth(array('MyAuth', 'isValid'), array('MyAuth', 'getUserData'));
  87. * $auth->start();
  88. *
  89. * if (!$auth->isValid) {
  90. * // decide what to do with unauthenticated user
  91. * } else {
  92. * echo 'Welcome back, ' . $auth->username . '!';
  93. * }
  94. * </code>
  95. *
  96. * @category Phly
  97. * @subcategory Phly_Auth
  98. * @subpackage Phly_Auth
  99. * @copyright 2006 - Present, Matthew Weier O'Phinney
  100. * @author Matthew Weier O'Phinney <mweierophinney@gmail.com>
  101. * @version @release-version@
  102. */
  103. class Phly_Auth
  104. {
  105. /**
  106. * Configuration array, with keys:
  107. * - form_username (default: username)
  108. * - form_password (default: password)
  109. * - form_submit (default: login; a form variable that should be set
  110. * indicating that the form submitted is a login form)
  111. * - password_hash (default: md5; callback to use to hash password)
  112. * - session_var (default: _auth; name of session key holding authentication
  113. * session)
  114. * - session_idle (default: null; maximum number of seconds allowed between
  115. * requests before requiring a new login)
  116. * - session_length (default: null; maximum number of seconds a session is
  117. * valid)
  118. * - use_get (default: false; flag; whether or not to check for login form
  119. * variables in the $_GET array)
  120. *
  121. * @var array
  122. * @access protected
  123. */
  124. protected $_config = array(
  125. 'form_username' => 'username',
  126. 'form_password' => 'password',
  127. 'form_submit' => 'login',
  128. 'password_hash' => 'md5',
  129. 'session_var' => '_auth',
  130. 'session_idle' => null,
  131. 'session_length'=> null,
  132. 'use_get' => false
  133. );
  134.  
  135. /**
  136. * Authentication validation callback
  137. * @var mixed
  138. * @access protected
  139. */
  140. protected $_validator;
  141.  
  142. /**
  143. * getData callback (for retrieving user data)
  144. * @var mixed
  145. * @access protected
  146. */
  147. protected $_getData = null;
  148.  
  149. /**
  150. * Constructor
  151. *
  152. * Creates authentication object using passed validation callback and
  153. * optional getData callback (for retrieving user data).
  154. *
  155. * @access public
  156. * @param mixed $isValidCallback Authentication callback
  157. * @param mixed $getDataCallback Optional callback for retrieving user data
  158. * @return void
  159. * @throws Phly_Auth_Exception
  160. */
  161. public function __construct($isValidCallback, $getDataCallback = null)
  162. {
  163. if (!is_callable($isValidCallback)) {
  164. throw new Phly_Auth_Exception('Invalid validation callback');
  165. }
  166.  
  167. $this->_validator = $isValidCallback;
  168.  
  169. if ((null !== $getDataCallback)) {
  170. if (!is_callable($getDataCallback)) {
  171. throw new Phly_Auth_Exception('Invalid data retrieval callback');
  172. }
  173.  
  174. $this->_getData = $getDataCallback;
  175. }
  176. }
  177.  
  178. /**
  179. * Configuration
  180. *
  181. * Allows storing/retrieving authentication options.
  182. *
  183. * Passing no values returns the entire {@link $_config}.
  184. *
  185. * Passing a single string value returns the value associated with that key
  186. * in {@link $_config}.
  187. *
  188. * Passing two values associates the second value with the key specified in
  189. * the first in {@link $_config}.
  190. *
  191. * If all else fails, returns null.
  192. *
  193. * @access public
  194. * @param string $key Optional; configuration key
  195. * @param mixed $value Optional; value to store in $key
  196. * @return mixed
  197. */
  198. public function config()
  199. {
  200. $argc = func_num_args();
  201. if (0 == $argc) {
  202. return $this->_config;
  203. }
  204.  
  205. if (1 == $argc) {
  206. $key = func_get_arg(0);
  207. if (isset($this->_config[$key])) {
  208. return $this->_config[$key];
  209. }
  210. return null;
  211. }
  212.  
  213. if (2 == $argc) {
  214. $key = func_get_arg(0);
  215. $value = func_get_arg(1);
  216. if (is_string($key)) {
  217. $this->_config[$key] = $value;
  218. return true;
  219. }
  220. }
  221.  
  222. return false;
  223. }
  224.  
  225. /**
  226. * Starts session, if not already done, updating auth session timestamp if
  227. * present.
  228. *
  229. * @access public
  230. * @return void
  231. */
  232. public function start()
  233. {
  234. @session_start();
  235. }
  236.  
  237. /**
  238. * Checks whether a user has authenticated.
  239. *
  240. * First checks to see if the user exists in the session; then attempts to
  241. * log a person in.
  242. *
  243. * @access public
  244. * @return boolean
  245. */
  246. public function isValid()
  247. {
  248. $sessionVar = $this->config('session_var');
  249. if (isset($_SESSION[$sessionVar])
  250. && isset($_SESSION[$sessionVar]['authenticated'])
  251. && $_SESSION[$sessionVar]['authenticated'])
  252. {
  253. // We have a valid session; now check to see if it's expired
  254. $time = time();
  255.  
  256. // Have we exceeded maximum session length?
  257. $sessionLength = $this->config('session_length');
  258. if (null !== $sessionLength) {
  259. if (($time - $this->loginTime) > $sessionLength) {
  260. $this->logout();
  261. return false;
  262. }
  263. }
  264.  
  265. // Have we idled too long?
  266. $sessionIdle = $this->config('session_idle');
  267. if (null !== $sessionIdle) {
  268. if (($time - $this->timestamp) > $sessionIdle) {
  269. $this->logout();
  270. return false;
  271. }
  272. }
  273.  
  274. // Update session timestamp
  275. $_SESSION[$sessionVar]['timestamp'] = $time;
  276.  
  277. return true;
  278. }
  279.  
  280. return $this->_login();
  281. }
  282.  
  283. /**
  284. * Attempt to login a user
  285. *
  286. * Attempts to login a user via $_POST (or $_GET if 'use_get' config value
  287. * is set to true).
  288. *
  289. * Uses the config values:
  290. * - form_username: username element of form
  291. * - form_password: password element of form
  292. * - form_submit: name of submit button of form (or any other key that
  293. * should be utilized)
  294. *
  295. * Then applies 'password_hash' config callback to the password, and sends
  296. * username and password to isValid() method of container.
  297. *
  298. * @access protected
  299. * @return boolean
  300. * @throws Phly_Auth_Exception if unable to populate session data
  301. */
  302. protected function _login()
  303. {
  304. if ('cli' == php_sapi_name()) {
  305. global $_POST;
  306. }
  307. $vars = $_POST;
  308. if ($this->config('use_get')) {
  309. if ('cli' == php_sapi_name()) {
  310. global $_GET;
  311. }
  312. $vars = $_GET;
  313. }
  314.  
  315. $usernameVar = $this->config('form_username');
  316. $passwordVar = $this->config('form_password');
  317. $loginVar = $this->config('form_submit');
  318.  
  319. if (isset($vars[$loginVar])) {
  320. if (isset($vars[$usernameVar])) {
  321. $username = $vars[$usernameVar];
  322. }
  323. if (isset($vars[$passwordVar])) {
  324. $password = call_user_func($this->config('password_hash'), $vars[$passwordVar]);
  325. }
  326.  
  327. if (isset($username) && isset($password)) {
  328. if ($uid = call_user_func_array($this->_validator, array($username, $password)))
  329. {
  330. if (true !== $uid) {
  331. $username = $uid;
  332. }
  333. try {
  334. $sessVar = $this->config('session_var');
  335. $_SESSION[$sessVar] = array(
  336. 'authenticated' => true,
  337. 'username' => $username,
  338. 'loginTime' => time(),
  339. 'timestamp' => time(),
  340. 'sessionData' => array()
  341. );
  342. if (null !== $this->_getData) {
  343. $_SESSION[$sessVar]['userData'] = call_user_func($this->_getData, $username);
  344. }
  345. } catch (Exception $e) {
  346. throw new Phly_Auth_Exception('Error populating session: ' . $e->getMessage());
  347. }
  348.  
  349. return true;
  350. }
  351. }
  352. }
  353.  
  354. return false;
  355. }
  356.  
  357. /**
  358. * Logout a user
  359. *
  360. * Logs out a user by unsetting the authentication session.
  361. *
  362. * @access public
  363. * @return void
  364. */
  365. public function logout()
  366. {
  367. unset($_SESSION[$this->config('session_var')]);
  368. }
  369.  
  370. /**
  371. * Retrieve a value from the session auth array
  372. *
  373. * Attempts to retrieve a value from the session auth array or the
  374. * sessionData array of the session auth array. If the value exists, it is
  375. * returned; otherwise, false is returned.
  376. *
  377. * @access public
  378. * @param string $key
  379. * @return mixed
  380. */
  381. public function __get($key)
  382. {
  383. $sessVar = $this->config('session_var');
  384. if (isset($_SESSION[$sessVar])
  385. && isset($_SESSION[$sessVar][$key]))
  386. {
  387. return $_SESSION[$sessVar][$key];
  388. } elseif (isset($_SESSION[$sessVar])
  389. && isset($_SESSION[$sessVar]['sessionData'])
  390. && isset($_SESSION[$sessVar]['sessionData'][$key]))
  391. {
  392. return $_SESSION[$sessVar]['sessionData'][$key];
  393. }
  394.  
  395. return false;
  396. }
  397.  
  398. /**
  399. * Set data in the sessionData array of the auth session
  400. *
  401. * @access public
  402. * @param string $key
  403. * @param mixed $value
  404. * @return boolean
  405. */
  406. public function __set($key, $value)
  407. {
  408. $sessVar = $this->config('session_var');
  409. if (isset($_SESSION[$sessVar])) {
  410. if (!isset($_SESSION[$sessVar]['sessionData'])) {
  411. $_SESSION[$sessVar]['sessionData'] = array(
  412. $key => $value
  413. );
  414. } else {
  415. $_SESSION[$sessVar]['sessionData'][$key] = $value;
  416. }
  417.  
  418. return true;
  419. }
  420.  
  421. return false;
  422. }
  423.  
  424. /**
  425. * Get session data from an authentication session
  426. *
  427. * Returns all session data stored in the sessionData array of the auth
  428. * session.
  429. *
  430. * To access individual values from the array, access them as class
  431. * properties.
  432. *
  433. * @access public
  434. * @return false|array
  435. */
  436. public function getSessionData()
  437. {
  438. $sessVar = $this->config('session_var');
  439.  
  440. if (isset($_SESSION[$sessVar])
  441. && isset($_SESSION[$sessVar]['sessionData']))
  442. {
  443. return $_SESSION[$sessVar]['sessionData'];
  444. }
  445.  
  446. return false;
  447. }
  448. }