PageRenderTime 60ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/application/libraries/Tank_auth.php

https://github.com/crow1170/mia-buzz
PHP | 658 lines | 372 code | 90 blank | 196 comment | 59 complexity | 72ee2e777452cb5a560f470c3d082951 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. <?php if (!defined('BASEPATH')) exit('No direct script access allowed');
  2. require_once('phpass-0.1/PasswordHash.php');
  3. define('STATUS_ACTIVATED', '1');
  4. define('STATUS_NOT_ACTIVATED', '0');
  5. /**
  6. * Tank_auth
  7. *
  8. * Authentication library for Code Igniter.
  9. *
  10. * @package Tank_auth
  11. * @author Ilya Konyukhov (http://konyukhov.com/soft/)
  12. * @version 1.0.9
  13. * @based on DX Auth by Dexcell (http://dexcell.shinsengumiteam.com/dx_auth)
  14. * @license MIT License Copyright (c) 2008 Erick Hartanto
  15. */
  16. class Tank_auth
  17. {
  18. private $error = array();
  19. function __construct()
  20. {
  21. $this->ci =& get_instance();
  22. $this->ci->load->config('tank_auth', TRUE);
  23. $this->ci->load->driver('session');
  24. $this->ci->load->database();
  25. $this->ci->load->model('tank_auth/users');
  26. // Try to autologin
  27. $this->autologin();
  28. }
  29. /**
  30. * Login user on the site. Return TRUE if login is successful
  31. * (user exists and activated, password is correct), otherwise FALSE.
  32. *
  33. * @param string (username or email or both depending on settings in config file)
  34. * @param string
  35. * @param bool
  36. * @return bool
  37. */
  38. function login($login, $password, $remember, $login_by_username, $login_by_email)
  39. {
  40. if ((strlen($login) > 0) AND (strlen($password) > 0)) {
  41. // Which function to use to login (based on config)
  42. if ($login_by_username AND $login_by_email) {
  43. $get_user_func = 'get_user_by_login';
  44. } else if ($login_by_username) {
  45. $get_user_func = 'get_user_by_username';
  46. } else {
  47. $get_user_func = 'get_user_by_email';
  48. }
  49. if (!is_null($user = $this->ci->users->$get_user_func($login))) { // login ok
  50. // Does password match hash in database?
  51. $hasher = new PasswordHash(
  52. $this->ci->config->item('phpass_hash_strength', 'tank_auth'),
  53. $this->ci->config->item('phpass_hash_portable', 'tank_auth'));
  54. if ($hasher->CheckPassword($password, $user->password)) { // password ok
  55. if ($user->banned == 1) { // fail - banned
  56. $this->error = array('banned' => $user->ban_reason);
  57. } else {
  58. $this->ci->session->set_userdata(array(
  59. 'user_id' => $user->id,
  60. 'username' => $user->username,
  61. 'status' => ($user->activated == 1) ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED,
  62. ));
  63. if ($user->activated == 0) { // fail - not activated
  64. $this->error = array('not_activated' => '');
  65. } else { // success
  66. if ($remember) {
  67. $this->create_autologin($user->id);
  68. }
  69. $this->clear_login_attempts($login);
  70. $this->ci->users->update_login_info(
  71. $user->id,
  72. $this->ci->config->item('login_record_ip', 'tank_auth'),
  73. $this->ci->config->item('login_record_time', 'tank_auth'));
  74. return TRUE;
  75. }
  76. }
  77. } else { // fail - wrong password
  78. $this->increase_login_attempt($login);
  79. $this->error = array('password' => 'auth_incorrect_password');
  80. }
  81. } else { // fail - wrong login
  82. $this->increase_login_attempt($login);
  83. $this->error = array('login' => 'auth_incorrect_login');
  84. }
  85. }
  86. return FALSE;
  87. }
  88. /**
  89. * Logout user from the site
  90. *
  91. * @return void
  92. */
  93. function logout()
  94. {
  95. $this->delete_autologin();
  96. // See http://codeigniter.com/forums/viewreply/662369/ as the reason for the next line
  97. $this->ci->session->set_userdata(array('user_id' => '', 'username' => '', 'status' => ''));
  98. $this->ci->session->sess_destroy();
  99. }
  100. /**
  101. * Check if user logged in. Also test if user is activated or not.
  102. *
  103. * @param bool
  104. * @return bool
  105. */
  106. function is_logged_in($activated = TRUE)
  107. {
  108. return $this->ci->session->userdata('status') === ($activated ? STATUS_ACTIVATED : STATUS_NOT_ACTIVATED);
  109. }
  110. /**
  111. * Get user_id
  112. *
  113. * @return string
  114. */
  115. function get_user_id()
  116. {
  117. return $this->ci->session->userdata('user_id');
  118. }
  119. /**
  120. * Get user_email
  121. *
  122. * @return string
  123. */
  124. function get_user_email()
  125. {
  126. if(!$this->get_user_id()){return false;}
  127. $user = $this->ci->users->get_user_by_id($this->get_user_id());
  128. return $user->email;
  129. }
  130. /**
  131. * Get username
  132. *
  133. * @return string
  134. */
  135. function get_username()
  136. {
  137. return $this->ci->session->userdata('username');
  138. }
  139. /**
  140. * Create new user on the site and return some data about it:
  141. * user_id, username, password, email, new_email_key (if any).
  142. *
  143. * @param string
  144. * @param string
  145. * @param string
  146. * @param bool
  147. * @return array
  148. */
  149. function create_user($username, $email, $password, $email_activation)
  150. {
  151. if ((strlen($username) > 0) AND !$this->ci->users->is_username_available($username)) {
  152. $this->error = array('username' => 'auth_username_in_use');
  153. } elseif (!$this->ci->users->is_email_available($email)) {
  154. $this->error = array('email' => 'auth_email_in_use');
  155. } else {
  156. // Hash password using phpass
  157. $hasher = new PasswordHash(
  158. $this->ci->config->item('phpass_hash_strength', 'tank_auth'),
  159. $this->ci->config->item('phpass_hash_portable', 'tank_auth'));
  160. $hashed_password = $hasher->HashPassword($password);
  161. $data = array(
  162. 'username' => $username,
  163. 'password' => $hashed_password,
  164. 'email' => $email,
  165. 'last_ip' => $this->ci->input->ip_address(),
  166. );
  167. if ($email_activation) {
  168. $data['new_email_key'] = md5(rand().microtime());
  169. }
  170. if (!is_null($res = $this->ci->users->create_user($data, !$email_activation))) {
  171. $data['user_id'] = $res['user_id'];
  172. $data['password'] = $password;
  173. unset($data['last_ip']);
  174. return $data;
  175. }
  176. }
  177. return NULL;
  178. }
  179. /**
  180. * Check if username available for registering.
  181. * Can be called for instant form validation.
  182. *
  183. * @param string
  184. * @return bool
  185. */
  186. function is_username_available($username)
  187. {
  188. return ((strlen($username) > 0) AND $this->ci->users->is_username_available($username));
  189. }
  190. /**
  191. * Check if email available for registering.
  192. * Can be called for instant form validation.
  193. *
  194. * @param string
  195. * @return bool
  196. */
  197. function is_email_available($email)
  198. {
  199. return ((strlen($email) > 0) AND $this->ci->users->is_email_available($email));
  200. }
  201. /**
  202. * Change email for activation and return some data about user:
  203. * user_id, username, email, new_email_key.
  204. * Can be called for not activated users only.
  205. *
  206. * @param string
  207. * @return array
  208. */
  209. function change_email($email)
  210. {
  211. $user_id = $this->ci->session->userdata('user_id');
  212. if (!is_null($user = $this->ci->users->get_user_by_id($user_id, FALSE))) {
  213. $data = array(
  214. 'user_id' => $user_id,
  215. 'username' => $user->username,
  216. 'email' => $email,
  217. );
  218. if (strtolower($user->email) == strtolower($email)) { // leave activation key as is
  219. $data['new_email_key'] = $user->new_email_key;
  220. return $data;
  221. } elseif ($this->ci->users->is_email_available($email)) {
  222. $data['new_email_key'] = md5(rand().microtime());
  223. $this->ci->users->set_new_email($user_id, $email, $data['new_email_key'], FALSE);
  224. return $data;
  225. } else {
  226. $this->error = array('email' => 'auth_email_in_use');
  227. }
  228. }
  229. return NULL;
  230. }
  231. /**
  232. * Activate user using given key
  233. *
  234. * @param string
  235. * @param string
  236. * @param bool
  237. * @return bool
  238. */
  239. function activate_user($user_id, $activation_key, $activate_by_email = TRUE)
  240. {
  241. $this->ci->users->purge_na($this->ci->config->item('email_activation_expire', 'tank_auth'));
  242. if ((strlen($user_id) > 0) AND (strlen($activation_key) > 0)) {
  243. return $this->ci->users->activate_user($user_id, $activation_key, $activate_by_email);
  244. }
  245. return FALSE;
  246. }
  247. /**
  248. * Set new password key for user and return some data about user:
  249. * user_id, username, email, new_pass_key.
  250. * The password key can be used to verify user when resetting his/her password.
  251. *
  252. * @param string
  253. * @return array
  254. */
  255. function forgot_password($login)
  256. {
  257. if (strlen($login) > 0) {
  258. if (!is_null($user = $this->ci->users->get_user_by_login($login))) {
  259. $data = array(
  260. 'user_id' => $user->id,
  261. 'username' => $user->username,
  262. 'email' => $user->email,
  263. 'new_pass_key' => md5(rand().microtime()),
  264. );
  265. $this->ci->users->set_password_key($user->id, $data['new_pass_key']);
  266. return $data;
  267. } else {
  268. $this->error = array('login' => 'auth_incorrect_email_or_username');
  269. }
  270. }
  271. return NULL;
  272. }
  273. /**
  274. * Check if given password key is valid and user is authenticated.
  275. *
  276. * @param string
  277. * @param string
  278. * @return bool
  279. */
  280. function can_reset_password($user_id, $new_pass_key)
  281. {
  282. if ((strlen($user_id) > 0) AND (strlen($new_pass_key) > 0)) {
  283. return $this->ci->users->can_reset_password(
  284. $user_id,
  285. $new_pass_key,
  286. $this->ci->config->item('forgot_password_expire', 'tank_auth'));
  287. }
  288. return FALSE;
  289. }
  290. /**
  291. * Replace user password (forgotten) with a new one (set by user)
  292. * and return some data about it: user_id, username, new_password, email.
  293. *
  294. * @param string
  295. * @param string
  296. * @return bool
  297. */
  298. function reset_password($user_id, $new_pass_key, $new_password)
  299. {
  300. if ((strlen($user_id) > 0) AND (strlen($new_pass_key) > 0) AND (strlen($new_password) > 0)) {
  301. if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) {
  302. // Hash password using phpass
  303. $hasher = new PasswordHash(
  304. $this->ci->config->item('phpass_hash_strength', 'tank_auth'),
  305. $this->ci->config->item('phpass_hash_portable', 'tank_auth'));
  306. $hashed_password = $hasher->HashPassword($new_password);
  307. if ($this->ci->users->reset_password(
  308. $user_id,
  309. $hashed_password,
  310. $new_pass_key,
  311. $this->ci->config->item('forgot_password_expire', 'tank_auth'))) { // success
  312. // Clear all user's autologins
  313. $this->ci->load->model('tank_auth/user_autologin');
  314. $this->ci->user_autologin->clear($user->id);
  315. return array(
  316. 'user_id' => $user_id,
  317. 'username' => $user->username,
  318. 'email' => $user->email,
  319. 'new_password' => $new_password,
  320. );
  321. }
  322. }
  323. }
  324. return NULL;
  325. }
  326. /**
  327. * Change user password (only when user is logged in)
  328. *
  329. * @param string
  330. * @param string
  331. * @return bool
  332. */
  333. function change_password($old_pass, $new_pass)
  334. {
  335. $user_id = $this->ci->session->userdata('user_id');
  336. if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) {
  337. // Check if old password correct
  338. $hasher = new PasswordHash(
  339. $this->ci->config->item('phpass_hash_strength', 'tank_auth'),
  340. $this->ci->config->item('phpass_hash_portable', 'tank_auth'));
  341. if ($hasher->CheckPassword($old_pass, $user->password)) { // success
  342. // Hash new password using phpass
  343. $hashed_password = $hasher->HashPassword($new_pass);
  344. // Replace old password with new one
  345. $this->ci->users->change_password($user_id, $hashed_password);
  346. return TRUE;
  347. } else { // fail
  348. $this->error = array('old_password' => 'auth_incorrect_password');
  349. }
  350. }
  351. return FALSE;
  352. }
  353. /**
  354. * Change user email (only when user is logged in) and return some data about user:
  355. * user_id, username, new_email, new_email_key.
  356. * The new email cannot be used for login or notification before it is activated.
  357. *
  358. * @param string
  359. * @param string
  360. * @return array
  361. */
  362. function set_new_email($new_email, $password)
  363. {
  364. $user_id = $this->ci->session->userdata('user_id');
  365. if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) {
  366. // Check if password correct
  367. $hasher = new PasswordHash(
  368. $this->ci->config->item('phpass_hash_strength', 'tank_auth'),
  369. $this->ci->config->item('phpass_hash_portable', 'tank_auth'));
  370. if ($hasher->CheckPassword($password, $user->password)) { // success
  371. $data = array(
  372. 'user_id' => $user_id,
  373. 'username' => $user->username,
  374. 'new_email' => $new_email,
  375. );
  376. if ($user->email == $new_email) {
  377. $this->error = array('email' => 'auth_current_email');
  378. } elseif ($user->new_email == $new_email) { // leave email key as is
  379. $data['new_email_key'] = $user->new_email_key;
  380. return $data;
  381. } elseif ($this->ci->users->is_email_available($new_email)) {
  382. $data['new_email_key'] = md5(rand().microtime());
  383. $this->ci->users->set_new_email($user_id, $new_email, $data['new_email_key'], TRUE);
  384. return $data;
  385. } else {
  386. $this->error = array('email' => 'auth_email_in_use');
  387. }
  388. } else { // fail
  389. $this->error = array('password' => 'auth_incorrect_password');
  390. }
  391. }
  392. return NULL;
  393. }
  394. /**
  395. * Activate new email, if email activation key is valid.
  396. *
  397. * @param string
  398. * @param string
  399. * @return bool
  400. */
  401. function activate_new_email($user_id, $new_email_key)
  402. {
  403. if ((strlen($user_id) > 0) AND (strlen($new_email_key) > 0)) {
  404. return $this->ci->users->activate_new_email(
  405. $user_id,
  406. $new_email_key);
  407. }
  408. return FALSE;
  409. }
  410. /**
  411. * Delete user from the site (only when user is logged in)
  412. *
  413. * @param string
  414. * @return bool
  415. */
  416. function delete_user($password)
  417. {
  418. $user_id = $this->ci->session->userdata('user_id');
  419. if (!is_null($user = $this->ci->users->get_user_by_id($user_id, TRUE))) {
  420. // Check if password correct
  421. $hasher = new PasswordHash(
  422. $this->ci->config->item('phpass_hash_strength', 'tank_auth'),
  423. $this->ci->config->item('phpass_hash_portable', 'tank_auth'));
  424. if ($hasher->CheckPassword($password, $user->password)) { // success
  425. $this->ci->users->delete_user($user_id);
  426. $this->logout();
  427. return TRUE;
  428. } else { // fail
  429. $this->error = array('password' => 'auth_incorrect_password');
  430. }
  431. }
  432. return FALSE;
  433. }
  434. /**
  435. * Get error message.
  436. * Can be invoked after any failed operation such as login or register.
  437. *
  438. * @return string
  439. */
  440. function get_error_message()
  441. {
  442. return $this->error;
  443. }
  444. /**
  445. * Save data for user's autologin
  446. *
  447. * @param int
  448. * @return bool
  449. */
  450. private function create_autologin($user_id)
  451. {
  452. $this->ci->load->helper('cookie');
  453. $key = substr(md5(uniqid(rand().get_cookie($this->ci->config->item('sess_cookie_name')))), 0, 16);
  454. $this->ci->load->model('tank_auth/user_autologin');
  455. $this->ci->user_autologin->purge($user_id);
  456. if ($this->ci->user_autologin->set($user_id, md5($key))) {
  457. set_cookie(array(
  458. 'name' => $this->ci->config->item('autologin_cookie_name', 'tank_auth'),
  459. 'value' => serialize(array('user_id' => $user_id, 'key' => $key)),
  460. 'expire' => $this->ci->config->item('autologin_cookie_life', 'tank_auth'),
  461. ));
  462. return TRUE;
  463. }
  464. return FALSE;
  465. }
  466. /**
  467. * Clear user's autologin data
  468. *
  469. * @return void
  470. */
  471. private function delete_autologin()
  472. {
  473. $this->ci->load->helper('cookie');
  474. if ($cookie = get_cookie($this->ci->config->item('autologin_cookie_name', 'tank_auth'), TRUE)) {
  475. $data = unserialize($cookie);
  476. $this->ci->load->model('tank_auth/user_autologin');
  477. $this->ci->user_autologin->delete($data['user_id'], md5($data['key']));
  478. delete_cookie($this->ci->config->item('autologin_cookie_name', 'tank_auth'));
  479. }
  480. }
  481. /**
  482. * Login user automatically if he/she provides correct autologin verification
  483. *
  484. * @return void
  485. */
  486. private function autologin()
  487. {
  488. if (!$this->is_logged_in() AND !$this->is_logged_in(FALSE)) { // not logged in (as any user)
  489. $this->ci->load->helper('cookie');
  490. if ($cookie = get_cookie($this->ci->config->item('autologin_cookie_name', 'tank_auth'), TRUE)) {
  491. $data = unserialize($cookie);
  492. if (isset($data['key']) AND isset($data['user_id'])) {
  493. $this->ci->load->model('tank_auth/user_autologin');
  494. if (!is_null($user = $this->ci->user_autologin->get($data['user_id'], md5($data['key'])))) {
  495. // Login user
  496. $this->ci->session->set_userdata(array(
  497. 'user_id' => $user->id,
  498. 'username' => $user->username,
  499. 'status' => STATUS_ACTIVATED,
  500. ));
  501. // Renew users cookie to prevent it from expiring
  502. set_cookie(array(
  503. 'name' => $this->ci->config->item('autologin_cookie_name', 'tank_auth'),
  504. 'value' => $cookie,
  505. 'expire' => $this->ci->config->item('autologin_cookie_life', 'tank_auth'),
  506. ));
  507. $this->ci->users->update_login_info(
  508. $user->id,
  509. $this->ci->config->item('login_record_ip', 'tank_auth'),
  510. $this->ci->config->item('login_record_time', 'tank_auth'));
  511. return TRUE;
  512. }
  513. }
  514. }
  515. }
  516. return FALSE;
  517. }
  518. /**
  519. * Check if login attempts exceeded max login attempts (specified in config)
  520. *
  521. * @param string
  522. * @return bool
  523. */
  524. function is_max_login_attempts_exceeded($login)
  525. {
  526. if ($this->ci->config->item('login_count_attempts', 'tank_auth')) {
  527. $this->ci->load->model('tank_auth/login_attempts');
  528. return $this->ci->login_attempts->get_attempts_num($this->ci->input->ip_address(), $login)
  529. >= $this->ci->config->item('login_max_attempts', 'tank_auth');
  530. }
  531. return FALSE;
  532. }
  533. /**
  534. * Increase number of attempts for given IP-address and login
  535. * (if attempts to login is being counted)
  536. *
  537. * @param string
  538. * @return void
  539. */
  540. private function increase_login_attempt($login)
  541. {
  542. if ($this->ci->config->item('login_count_attempts', 'tank_auth')) {
  543. if (!$this->is_max_login_attempts_exceeded($login)) {
  544. $this->ci->load->model('tank_auth/login_attempts');
  545. $this->ci->login_attempts->increase_attempt($this->ci->input->ip_address(), $login);
  546. }
  547. }
  548. }
  549. /**
  550. * Clear all attempt records for given IP-address and login
  551. * (if attempts to login is being counted)
  552. *
  553. * @param string
  554. * @return void
  555. */
  556. private function clear_login_attempts($login)
  557. {
  558. if ($this->ci->config->item('login_count_attempts', 'tank_auth')) {
  559. $this->ci->load->model('tank_auth/login_attempts');
  560. $this->ci->login_attempts->clear_attempts(
  561. $this->ci->input->ip_address(),
  562. $login,
  563. $this->ci->config->item('login_attempt_expire', 'tank_auth'));
  564. }
  565. }
  566. }
  567. /* End of file Tank_auth.php */
  568. /* Location: ./application/libraries/Tank_auth.php */