PageRenderTime 63ms CodeModel.GetById 34ms RepoModel.GetById 1ms app.codeStats 0ms

/a1/libraries/A1.php

https://github.com/Wouterrr/kohanamodules2.3.2
PHP | 288 lines | 137 code | 48 blank | 103 comment | 13 complexity | bde5f677444ac63436580ac142e82eae MD5 | raw file
  1. <?php
  2. /**
  3. * User AUTHENTICATION library. Handles user login and logout, as well as secure
  4. * password hashing.
  5. *
  6. * Based on Kohana's AUTH library and Fred Wu's AuthLite library:
  7. *
  8. * @package Auth
  9. * @author Kohana Team
  10. * @copyright (c) 2007 Kohana Team
  11. * @license http://kohanaphp.com/license.html
  12. *
  13. * @package Layerful
  14. * @subpackage Modules
  15. * @author Layerful Team <http://layerful.org/>
  16. * @author Fred Wu <fred@beyondcoding.com>
  17. * @copyright BeyondCoding
  18. * @license http://layerful.org/license MIT
  19. * @since 0.3.0
  20. */
  21. class A1_Core {
  22. protected $config_name;
  23. protected $session;
  24. protected $config;
  25. protected $user_model;
  26. protected $columns;
  27. /**
  28. * Create an instance of A1.
  29. *
  30. * @return object
  31. */
  32. public static function factory($config_name = 'a1')
  33. {
  34. return new A1($config_name);
  35. }
  36. /**
  37. * Return a static instance of A1.
  38. *
  39. * @return object
  40. */
  41. public static function instance($config_name = 'a1')
  42. {
  43. static $instance;
  44. // Load the A1 instance
  45. empty($instance[$config_name]) and $instance[$config_name] = new A1($config_name);
  46. return $instance[$config_name];
  47. }
  48. /**
  49. * Loads Session and configuration options.
  50. *
  51. * @return void
  52. */
  53. public function __construct($config_name = 'a1')
  54. {
  55. $this->config_name = $config_name;
  56. $this->session = Session::instance();
  57. $this->config = Kohana::config($config_name);
  58. $this->user_model = $this->config['user_model'];
  59. $this->columns = $this->config['columns'];
  60. // Clean up the salt pattern and split it into an array
  61. $this->config['salt_pattern'] = preg_split('/,\s*/', $this->config['salt_pattern']);
  62. Kohana::log('debug', 'A1 Library loaded');
  63. }
  64. /**
  65. * Returns TRUE is a user is currently logged in
  66. *
  67. * @return boolean
  68. */
  69. public function logged_in()
  70. {
  71. return is_object($this->get_user());
  72. }
  73. /**
  74. * Returns the user - if any
  75. *
  76. * @return object / FALSE
  77. */
  78. public function get_user()
  79. {
  80. // Get the user from the session
  81. $user = $this->session->get($this->config['session_key']);
  82. // User found in session, return
  83. if(is_object($user))
  84. return $user;
  85. // Look for user in cookie
  86. if( $this->config['lifetime'])
  87. {
  88. if ( ($token = cookie::get('a1_'.$this->config_name.'_autologin')) )
  89. {
  90. $token = explode('.',$token);
  91. if (count($token) === 2 AND is_string($token[0]) AND is_numeric($token[1]))
  92. {
  93. // Search user on user ID and token. Because user ID is primary key, this is much faster than
  94. // searching on just the token.
  95. $user = ORM::factory($this->user_model)->where($this->columns['token'],$token[0])->find($token[1]);
  96. // Found user, complete login and return
  97. if ($user->loaded)
  98. {
  99. $this->complete_login($user,TRUE);
  100. return $user;
  101. }
  102. }
  103. }
  104. }
  105. // No user found, return false
  106. return FALSE;
  107. }
  108. protected function complete_login($user, $remember = FALSE)
  109. {
  110. if ($remember === TRUE AND $this->config['lifetime'])
  111. {
  112. // Create token
  113. $token = text::random('alnum', 32);
  114. $user->{$this->columns['token']} = $token;
  115. // TODO: find a better way to store used_id in cookie
  116. cookie::set('a1_'.$this->config_name.'_autologin', $token . '.' . $user->primary_key_value, $this->config['lifetime']);
  117. }
  118. if(isset($this->columns['last_login']))
  119. {
  120. $user->{$this->columns['last_login']} = time();
  121. }
  122. if(isset($this->columns['logins']))
  123. {
  124. $user->{$this->columns['logins']}++;
  125. }
  126. $user->save();
  127. // Regenerate session (prevents session fixation attacks)
  128. $this->session->regenerate();
  129. $this->session->set($this->config['session_key'], $user);
  130. }
  131. /**
  132. * Attempt to log in a user by using an ORM object and plain-text password.
  133. *
  134. * @param string username to log in
  135. * @param string password to check against
  136. * @param boolean enable auto-login
  137. * @return boolean
  138. */
  139. public function login($username, $password, $remember = FALSE)
  140. {
  141. if (empty($password))
  142. return FALSE;
  143. $user = is_object($username) ? $username : ORM::factory($this->user_model)->where($this->columns['username'], $username)->find();
  144. $salt = $this->find_salt($user->{$this->columns['password']});
  145. if($this->hash_password($password, $salt) === $user->{$this->columns['password']})
  146. {
  147. $this->complete_login($user,$remember);
  148. return TRUE;
  149. }
  150. return FALSE;
  151. }
  152. /**
  153. * Log out a user by removing the related session variables.
  154. *
  155. * @param boolean completely destroy the session
  156. * @return boolean
  157. */
  158. public function logout($destroy = FALSE)
  159. {
  160. if (cookie::get('a1_'.$this->config_name.'_autologin'))
  161. {
  162. cookie::delete('a1_'.$this->config_name.'_autologin');
  163. }
  164. if ($destroy === TRUE)
  165. {
  166. // Destroy the session completely
  167. $this->session->destroy();
  168. }
  169. else
  170. {
  171. // Remove the user from the session
  172. $this->session->delete($this->config['session_key']);
  173. // Regenerate session_id
  174. $this->session->regenerate();
  175. }
  176. return ! $this->logged_in();
  177. }
  178. /**
  179. * Creates a hashed password from a plaintext password, inserting salt
  180. * based on the configured salt pattern.
  181. *
  182. * @param string plaintext password
  183. * @return string hashed password string
  184. */
  185. public function hash_password($password, $salt = FALSE)
  186. {
  187. if ($salt === FALSE)
  188. {
  189. // Create a salt seed, same length as the number of offsets in the pattern
  190. $salt = substr($this->hash(uniqid(NULL, TRUE)), 0, count($this->config['salt_pattern']));
  191. }
  192. // Password hash that the salt will be inserted into
  193. $hash = $this->hash($salt.$password);
  194. // Change salt to an array
  195. $salt = str_split($salt, 1);
  196. // Returned password
  197. $password = '';
  198. // Used to calculate the length of splits
  199. $last_offset = 0;
  200. foreach ($this->config['salt_pattern'] as $offset)
  201. {
  202. // Split a new part of the hash off
  203. $part = substr($hash, 0, $offset - $last_offset);
  204. // Cut the current part out of the hash
  205. $hash = substr($hash, $offset - $last_offset);
  206. // Add the part to the password, appending the salt character
  207. $password .= $part.array_shift($salt);
  208. // Set the last offset to the current offset
  209. $last_offset = $offset;
  210. }
  211. // Return the password, with the remaining hash appended
  212. return $password.$hash;
  213. }
  214. /**
  215. * Perform a hash, using the configured method.
  216. *
  217. * @param string string to hash
  218. * @return string
  219. */
  220. public function hash($str)
  221. {
  222. return hash($this->config['hash_method'], $str);
  223. }
  224. /**
  225. * Finds the salt from a password, based on the configured salt pattern.
  226. *
  227. * @param string hashed password
  228. * @return string
  229. */
  230. public function find_salt($password)
  231. {
  232. $salt = '';
  233. foreach ($this->config['salt_pattern'] as $i => $offset)
  234. {
  235. // Find salt characters, take a good long look...
  236. $salt .= substr($password, $offset + $i, 1);
  237. }
  238. return $salt;
  239. }
  240. } // End A1