PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/libraries/Auth.php

https://bitbucket.org/gyo/authentication-library
PHP | 450 lines | 194 code | 85 blank | 171 comment | 28 complexity | d995d19bae1141a0c0a00de6e08b60ad MD5 | raw file
  1. <?php
  2. /**
  3. * Auth - Secure, clean Authentication library for CodeIgniter
  4. *
  5. * @package Auth
  6. * @category Libraries
  7. * @author Giordano Piazza
  8. * @link http://www.giordanopiazza.com
  9. * @version 0.4
  10. * @copyright Copyright (c) 2009-2010, Giordano Piazza
  11. *
  12. */
  13. /*
  14. TODO
  15. - Login attempts
  16. - Encrypt/Decrypt cookies and database
  17. NOTES
  18. - security reminder: check login attempts from multiple IPs to the same username
  19. - remember me: if last ip mask doesn't match current, force the user to enter the password
  20. */
  21. class Auth
  22. {
  23. protected $ci; // The CI object
  24. protected $config; // The config items
  25. protected $users_table; // The user table (prefix + config)
  26. protected $groups_table; // The group table (prefix + config)
  27. protected $remember_me_cookie; // The cookie used for remember me autologin
  28. /**
  29. * Auth constructor
  30. *
  31. * @access public
  32. */
  33. public function __construct()
  34. {
  35. $this->ci =& get_instance();
  36. $this->ci->load->database();
  37. $this->ci->load->library('session');
  38. $this->ci->load->library('security');
  39. $this->ci->config->load('auth');
  40. $this->users_table = $this->ci->config->item('users_table');
  41. $this->groups_table = $this->ci->config->item('groups_table');
  42. // Check remember-me cookie if enabled
  43. if ($this->ci->config->item('remember_me'))
  44. {
  45. if(!$this->logged_in())
  46. {
  47. $this->verify_cookie();
  48. }
  49. }
  50. }
  51. /**
  52. * Log a user in
  53. *
  54. *
  55. * @access public
  56. * @param string
  57. * @param string
  58. * @param bool
  59. * @return bool
  60. */
  61. public function login($identity = false, $password = false, $remember_me = false)
  62. {
  63. // TODO: better login attempts handling in users table
  64. /*if((array_key_exists('login_attempts', $_COOKIE)) && ($_COOKIE['login_attempts'] >= 5))
  65. {
  66. echo $this->ci->lang->line('max_login_attempts_error');
  67. }*/
  68. // Check if identity and password are there
  69. if ($identity === false || $password === false)
  70. {
  71. return false;
  72. }
  73. // Get user identity
  74. $query = $this->get_identity($identity);
  75. // If the identity is found
  76. if ($query->num_rows() == 1)
  77. {
  78. // Get query result
  79. $result = $query->row();
  80. if (!empty($result->activation_code)) { return false; }
  81. // Hashing password with user's email as Salt (against rainbow tables)
  82. $password = $this->hash($password, $result->email);
  83. // Check if the passwords match
  84. if ($result->password === $password)
  85. {
  86. // Set the identity and the logged_in flag in the session data
  87. $this->ci->session->set_userdata(array('identity' => $identity,
  88. 'logged_in' => true));
  89. //$this->ci->session->set_userdata('logged_in', true);
  90. // If remember-me is enabled, create the cookie
  91. if ($remember_me) $this->make_cookie($identity);
  92. // Force the session id to refresh
  93. $this->ci->session->sess_time_to_update = 0;
  94. $this->ci->session->sess_update();
  95. return true;
  96. }
  97. else
  98. {
  99. $this->session->unset_userdata('logged_in');
  100. return false;
  101. }
  102. }
  103. return false;
  104. }
  105. /**
  106. * Logout - logs a user out
  107. *
  108. * @access public
  109. * @return void
  110. */
  111. public function logout()
  112. {
  113. // destroy session
  114. $this->ci->session->sess_destroy();
  115. // delete cookie
  116. $this->delete_cookie();
  117. }
  118. /**
  119. * Register a new user
  120. *
  121. * Adds the main user information, and the additional profile columns if specified
  122. *
  123. * @access public
  124. * @param string
  125. * @return bool
  126. */
  127. public function register($username = false, $password = false, $email = false, $profile_fields = false)
  128. {
  129. if ($username === false || $password === false || $email === false)
  130. {
  131. return false;
  132. }
  133. // Add info to the users table
  134. $data = array('username' => $username,
  135. 'password' => $this->hash($password, $email),
  136. 'email' => $email,
  137. 'activation_code' => sha1(microtime().$username.$email),
  138. 'created' => date("y-m-d H:i:s", time()),
  139. 'last_ip' => $this->ci->input->ip_address());
  140. $this->ci->db->insert($this->users_table, $data);
  141. // Add info to the profiles table - not yet used
  142. if ($this->ci->config->item('profiles_table') != '')
  143. {
  144. $id = $this->ci->db->insert_id();
  145. $data = array($this->ci->config->item('join_table') => $id);
  146. if (!empty($profile_fields))
  147. {
  148. foreach ($profile_fields as $input)
  149. $data[$input] = $this->ci->input->post($input, true);
  150. }
  151. $this->ci->db->insert($this->ci->config->item('profiles_table'), $data);
  152. return ($this->ci->db->affected_rows() > 0) ? true : false;
  153. }
  154. }
  155. /**
  156. * Check if a user is logged in
  157. *
  158. * Look in the session data and return the 'logged_in' value
  159. *
  160. * @access public
  161. * @return bool
  162. */
  163. public function logged_in()
  164. {
  165. return ($this->ci->session->userdata('logged_in')) ? true : false;
  166. }
  167. /**
  168. * Activate the user with the code sent by email
  169. *
  170. * Check if the provided activation code exists in the database and empty the field to activate the user
  171. *
  172. * @access public
  173. * @param string
  174. * @return bool
  175. */
  176. public function activate($code = false)
  177. {
  178. if ($code === false) return false;
  179. $code = $this->ci->security->xss_clean($code);
  180. $this->ci->db->where('activation_code', $code)
  181. ->limit(1)
  182. ->update($this->users_table, array('activation_code' => ''));
  183. return ($this->ci->db->affected_rows() > 0) ? true : false;
  184. }
  185. /**
  186. * Get user identity
  187. *
  188. * @access public
  189. * @param string
  190. * @return mixed : query-result or false
  191. */
  192. public function get_identity($identity = false)
  193. {
  194. if ($identity === false) return false;
  195. // get the columns to use as identity
  196. $identity_columns = $this->ci->config->item('identity_columns');
  197. // select the fields separating them with a comma
  198. $this->ci->db->select(implode(", ", $identity_columns).', id, password, activation_code');
  199. // Prepare the query with all the available identities
  200. foreach ($identity_columns as $column)
  201. $this->ci->db->or_where($column.' =', $identity);
  202. // finally get and return user info
  203. $query = $this->ci->db->get($this->users_table, 1, 0);
  204. return ($query->num_rows() === 1) ? $query : false;
  205. }
  206. /**
  207. * Verify if the user's identity already exists in the database
  208. *
  209. * @access public
  210. * @param string
  211. * @return bool
  212. */
  213. public function check_identity($identity = false)
  214. {
  215. return ($this->get_identity($identity) != false) ? true : false;
  216. }
  217. /**
  218. * Hash the user password with the encryption key, and the salt if set
  219. *
  220. * @access protected
  221. * @param string
  222. * @return string
  223. */
  224. protected function hash($str, $salt = false)
  225. {
  226. if ($salt) $str .= $salt;
  227. return hash($this->ci->config->item('hash_type'), $this->ci->config->item('salt').$str);
  228. }
  229. /**
  230. * Generate a 128 bit random uuid
  231. *
  232. * @access protected
  233. * @return string
  234. */
  235. protected function generate_token()
  236. {
  237. return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
  238. mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
  239. mt_rand( 0, 0x0fff ) | 0x4000,
  240. mt_rand( 0, 0x3fff ) | 0x8000,
  241. mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) );
  242. }
  243. /**
  244. * Verify that a user has a Remember-Me cookie.
  245. * If the cookie doesn't match the database, log the user out.
  246. *
  247. * @access protected
  248. * @return void
  249. */
  250. protected function verify_cookie()
  251. {
  252. //log_message('debug', "============== Verify Cookie started");
  253. $this->ci->load->library('encrypt');
  254. $this->ci->load->helper('cookie');
  255. // get cookie
  256. $cookie = $this->ci->input->cookie($this->ci->config->item('remember_me_cookie'), true);
  257. // decrypt cookie
  258. if ($cookie)
  259. {
  260. $cookie = $this->ci->encrypt->decode($cookie);
  261. $cookie = @unserialize(base64_decode($cookie));
  262. }
  263. // validate cookie
  264. if (is_array($cookie))
  265. {
  266. //log_message('debug', "============== Cookie found");
  267. $remember_me_table = $this->ci->config->item('remember_me_table');
  268. // get db data
  269. $query = $this->ci->db->select('identity, token, user_agent')
  270. ->where(array('identity' => $cookie['identity'], 'token' => $cookie['token']))
  271. ->limit(1)
  272. ->get($remember_me_table);
  273. $result = $query->row();
  274. // found the cookie in the db
  275. if ($query->num_rows() == 1)
  276. {
  277. //log_message('debug', "============== Cookie found in the DB");
  278. // double check if the cookie and the user agent match (maybe maniac?)
  279. if ($result->identity == $cookie['identity'] &&
  280. $result->token == $cookie['token'] &&
  281. $result->user_agent == $this->ci->input->user_agent())
  282. {
  283. // delete token from database
  284. $this->ci->db->where(array('identity' => $result->identity, 'token' => $result->token))
  285. ->limit(1)
  286. ->delete($remember_me_table);
  287. // Force the session id to refresh
  288. $this->ci->session->sess_time_to_update = 0;
  289. $this->ci->session->sess_update();
  290. // The user is now logged in, remember me is flagged to disallow some specific operations until a password is provided
  291. $data = array('identity' => $result->identity,
  292. 'logged_in' => true,
  293. 'remember_me' => true);
  294. $this->ci->session->set_userdata($data);
  295. // Make the Remember-me cookie
  296. $this->make_cookie($result->identity);
  297. //log_message('debug', "============== Cookie verified and recreated");
  298. return true;
  299. }
  300. /*else
  301. {
  302. //log_message('debug', "============== Cookie NOT verfied");
  303. // delete cookie
  304. //$this->delete_cookie();
  305. }*/
  306. }
  307. else
  308. {
  309. //log_message('debug', "============== Cookie NOT found in the DB");
  310. $this->logout();
  311. return false;
  312. }
  313. }
  314. else
  315. {
  316. //log_message('debug', "============== Cookie NOT found");
  317. return false;
  318. }
  319. }
  320. /**
  321. * Make a new Remember-me cookie
  322. *
  323. * @access private
  324. * @return void
  325. */
  326. protected function make_cookie($identity = false)
  327. {
  328. if ($identity === false) return false;
  329. // generate a new random token
  330. $token = $this->generate_token();
  331. $data = array('identity' => $identity,
  332. 'token' => $token,
  333. 'user_agent' => $this->ci->input->user_agent(),
  334. 'last_ip' => $this->ci->input->ip_address(),
  335. 'last_login' => date("y-m-d H:i:s", time()));
  336. // insert data into database
  337. $this->ci->db->insert($this->ci->config->item('remember_me_table'), $data);
  338. // prepare cookie data
  339. $cookie['identity'] = $identity;
  340. $cookie['token'] = $token;
  341. $cookie = base64_encode(serialize($cookie));
  342. // encrypt the cookie
  343. $identifier = $this->ci->encrypt->encode($cookie);
  344. // create the cookie
  345. setcookie($this->ci->config->item('remember_me_cookie'), $identifier, time()+$this->ci->config->item('remember_me_for'), '/', $this->ci->config->item('cookie_domain'));
  346. }
  347. /**
  348. * Delete Remember-me cookie
  349. *
  350. * @access private
  351. * @return void
  352. */
  353. protected function delete_cookie()
  354. {
  355. setcookie($this->ci->config->item('remember_me_cookie'), '0', time()-3600, '/', $this->ci->config->item('cookie_domain'));
  356. }
  357. } // class Auth
  358. // EOF: Auth.php