PageRenderTime 89ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/components/com_users/models/reset.php

https://github.com/kochen/joomla-rtl
PHP | 501 lines | 287 code | 85 blank | 129 comment | 34 complexity | 44537d0f8e2f595cf2e739624f66a150 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause, Apache-2.0
  1. <?php
  2. /**
  3. * @package Joomla.Site
  4. * @subpackage com_users
  5. *
  6. * @copyright Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE.txt
  8. */
  9. defined('_JEXEC') or die;
  10. /**
  11. * Rest model class for Users.
  12. *
  13. * @package Joomla.Site
  14. * @subpackage com_users
  15. * @since 1.5
  16. */
  17. class UsersModelReset extends JModelForm
  18. {
  19. /**
  20. * Method to get the password reset request form.
  21. *
  22. * @param array $data Data for the form.
  23. * @param boolean $loadData True if the form is to load its own data (default case), false if not.
  24. * @return JForm A JForm object on success, false on failure
  25. * @since 1.6
  26. */
  27. public function getForm($data = array(), $loadData = true)
  28. {
  29. // Get the form.
  30. $form = $this->loadForm('com_users.reset_request', 'reset_request', array('control' => 'jform', 'load_data' => $loadData));
  31. if (empty($form))
  32. {
  33. return false;
  34. }
  35. return $form;
  36. }
  37. /**
  38. * Method to get the password reset complete form.
  39. *
  40. * @param array $data Data for the form.
  41. * @param boolean $loadData True if the form is to load its own data (default case), false if not.
  42. * @return JForm A JForm object on success, false on failure
  43. * @since 1.6
  44. */
  45. public function getResetCompleteForm($data = array(), $loadData = true)
  46. {
  47. // Get the form.
  48. $form = $this->loadForm('com_users.reset_complete', 'reset_complete', $options = array('control' => 'jform'));
  49. if (empty($form))
  50. {
  51. return false;
  52. }
  53. return $form;
  54. }
  55. /**
  56. * Method to get the password reset confirm form.
  57. *
  58. * @param array $data Data for the form.
  59. * @param boolean $loadData True if the form is to load its own data (default case), false if not.
  60. *
  61. * @return JForm A JForm object on success, false on failure
  62. *
  63. * @since 1.6
  64. */
  65. public function getResetConfirmForm($data = array(), $loadData = true)
  66. {
  67. // Get the form.
  68. $form = $this->loadForm('com_users.reset_confirm', 'reset_confirm', $options = array('control' => 'jform'));
  69. if (empty($form))
  70. {
  71. return false;
  72. }
  73. else
  74. {
  75. $form->setValue('token', '', JFactory::getApplication()->input->get('token'));
  76. }
  77. return $form;
  78. }
  79. /**
  80. * Override preprocessForm to load the user plugin group instead of content.
  81. *
  82. * @param object A form object.
  83. * @param mixed The data expected for the form.
  84. * @throws Exception if there is an error in the form event.
  85. * @since 1.6
  86. */
  87. protected function preprocessForm(JForm $form, $data, $group = 'user')
  88. {
  89. parent::preprocessForm($form, $data, $group);
  90. }
  91. /**
  92. * Method to auto-populate the model state.
  93. *
  94. * Note. Calling getState in this method will result in recursion.
  95. *
  96. * @since 1.6
  97. */
  98. protected function populateState()
  99. {
  100. // Get the application object.
  101. $params = JFactory::getApplication()->getParams('com_users');
  102. // Load the parameters.
  103. $this->setState('params', $params);
  104. }
  105. /**
  106. * @since 1.6
  107. */
  108. public function processResetComplete($data)
  109. {
  110. // Get the form.
  111. $form = $this->getResetCompleteForm();
  112. $data['email'] = JStringPunycode::emailToPunycode($data['email']);
  113. // Check for an error.
  114. if ($form instanceof Exception)
  115. {
  116. return $form;
  117. }
  118. // Filter and validate the form data.
  119. $data = $form->filter($data);
  120. $return = $form->validate($data);
  121. // Check for an error.
  122. if ($return instanceof Exception)
  123. {
  124. return $return;
  125. }
  126. // Check the validation results.
  127. if ($return === false)
  128. {
  129. // Get the validation messages from the form.
  130. foreach ($form->getErrors() as $formError)
  131. {
  132. $this->setError($formError->getMessage());
  133. }
  134. return false;
  135. }
  136. // Get the token and user id from the confirmation process.
  137. $app = JFactory::getApplication();
  138. $token = $app->getUserState('com_users.reset.token', null);
  139. $userId = $app->getUserState('com_users.reset.user', null);
  140. // Check the token and user id.
  141. if (empty($token) || empty($userId))
  142. {
  143. return new JException(JText::_('COM_USERS_RESET_COMPLETE_TOKENS_MISSING'), 403);
  144. }
  145. // Get the user object.
  146. $user = JUser::getInstance($userId);
  147. // Check for a user and that the tokens match.
  148. if (empty($user) || $user->activation !== $token)
  149. {
  150. $this->setError(JText::_('COM_USERS_USER_NOT_FOUND'));
  151. return false;
  152. }
  153. // Make sure the user isn't blocked.
  154. if ($user->block)
  155. {
  156. $this->setError(JText::_('COM_USERS_USER_BLOCKED'));
  157. return false;
  158. }
  159. // Check if the user is reusing the current password if required to reset their password
  160. if ($user->requireReset == 1 && JUserHelper::verifyPassword($data['password1'], $user->password))
  161. {
  162. $this->setError(JText::_('JLIB_USER_ERROR_CANNOT_REUSE_PASSWORD'));
  163. return false;
  164. }
  165. // Update the user object.
  166. $user->password = JUserHelper::hashPassword($data['password1']);
  167. $user->activation = '';
  168. $user->password_clear = $data['password1'];
  169. // Save the user to the database.
  170. if (!$user->save(true))
  171. {
  172. return new JException(JText::sprintf('COM_USERS_USER_SAVE_FAILED', $user->getError()), 500);
  173. }
  174. // Flush the user data from the session.
  175. $app->setUserState('com_users.reset.token', null);
  176. $app->setUserState('com_users.reset.user', null);
  177. return true;
  178. }
  179. /**
  180. * @since 1.6
  181. */
  182. public function processResetConfirm($data)
  183. {
  184. // Get the form.
  185. $form = $this->getResetConfirmForm();
  186. $data['email'] = JStringPunycode::emailToPunycode($data['email']);
  187. // Check for an error.
  188. if ($form instanceof Exception)
  189. {
  190. return $form;
  191. }
  192. // Filter and validate the form data.
  193. $data = $form->filter($data);
  194. $return = $form->validate($data);
  195. // Check for an error.
  196. if ($return instanceof Exception)
  197. {
  198. return $return;
  199. }
  200. // Check the validation results.
  201. if ($return === false)
  202. {
  203. // Get the validation messages from the form.
  204. foreach ($form->getErrors() as $formError)
  205. {
  206. $this->setError($formError->getMessage());
  207. }
  208. return false;
  209. }
  210. // Find the user id for the given token.
  211. $db = $this->getDbo();
  212. $query = $db->getQuery(true)
  213. ->select('activation')
  214. ->select('id')
  215. ->select('block')
  216. ->from($db->quoteName('#__users'))
  217. ->where($db->quoteName('username') . ' = ' . $db->quote($data['username']));
  218. // Get the user id.
  219. $db->setQuery($query);
  220. try
  221. {
  222. $user = $db->loadObject();
  223. }
  224. catch (RuntimeException $e)
  225. {
  226. return new JException(JText::sprintf('COM_USERS_DATABASE_ERROR', $e->getMessage()), 500);
  227. }
  228. // Check for a user.
  229. if (empty($user))
  230. {
  231. $this->setError(JText::_('COM_USERS_USER_NOT_FOUND'));
  232. return false;
  233. }
  234. $parts = explode(':', $user->activation);
  235. $crypt = $parts[0];
  236. if (!isset($parts[1]))
  237. {
  238. $this->setError(JText::_('COM_USERS_USER_NOT_FOUND'));
  239. return false;
  240. }
  241. $salt = $parts[1];
  242. $testcrypt = JUserHelper::getCryptedPassword($data['token'], $salt, 'md5-hex');
  243. // Verify the token
  244. if (!($crypt == $testcrypt))
  245. {
  246. $this->setError(JText::_('COM_USERS_USER_NOT_FOUND'));
  247. return false;
  248. }
  249. // Make sure the user isn't blocked.
  250. if ($user->block)
  251. {
  252. $this->setError(JText::_('COM_USERS_USER_BLOCKED'));
  253. return false;
  254. }
  255. // Push the user data into the session.
  256. $app = JFactory::getApplication();
  257. $app->setUserState('com_users.reset.token', $crypt . ':' . $salt);
  258. $app->setUserState('com_users.reset.user', $user->id);
  259. return true;
  260. }
  261. /**
  262. * Method to start the password reset process.
  263. *
  264. * @since 1.6
  265. */
  266. public function processResetRequest($data)
  267. {
  268. $config = JFactory::getConfig();
  269. // Get the form.
  270. $form = $this->getForm();
  271. $data['email'] = JStringPunycode::emailToPunycode($data['email']);
  272. // Check for an error.
  273. if ($form instanceof Exception)
  274. {
  275. return $form;
  276. }
  277. // Filter and validate the form data.
  278. $data = $form->filter($data);
  279. $return = $form->validate($data);
  280. // Check for an error.
  281. if ($return instanceof Exception)
  282. {
  283. return $return;
  284. }
  285. // Check the validation results.
  286. if ($return === false)
  287. {
  288. // Get the validation messages from the form.
  289. foreach ($form->getErrors() as $formError)
  290. {
  291. $this->setError($formError->getMessage());
  292. }
  293. return false;
  294. }
  295. // Find the user id for the given email address.
  296. $db = $this->getDbo();
  297. $query = $db->getQuery(true)
  298. ->select('id')
  299. ->from($db->quoteName('#__users'))
  300. ->where($db->quoteName('email') . ' = ' . $db->quote($data['email']));
  301. // Get the user object.
  302. $db->setQuery($query);
  303. try
  304. {
  305. $userId = $db->loadResult();
  306. }
  307. catch (RuntimeException $e)
  308. {
  309. $this->setError(JText::sprintf('COM_USERS_DATABASE_ERROR', $e->getMessage()), 500);
  310. return false;
  311. }
  312. // Check for a user.
  313. if (empty($userId))
  314. {
  315. $this->setError(JText::_('COM_USERS_INVALID_EMAIL'));
  316. return false;
  317. }
  318. // Get the user object.
  319. $user = JUser::getInstance($userId);
  320. // Make sure the user isn't blocked.
  321. if ($user->block)
  322. {
  323. $this->setError(JText::_('COM_USERS_USER_BLOCKED'));
  324. return false;
  325. }
  326. // Make sure the user isn't a Super Admin.
  327. if ($user->authorise('core.admin'))
  328. {
  329. $this->setError(JText::_('COM_USERS_REMIND_SUPERADMIN_ERROR'));
  330. return false;
  331. }
  332. // Make sure the user has not exceeded the reset limit
  333. if (!$this->checkResetLimit($user))
  334. {
  335. $resetLimit = (int) JFactory::getApplication()->getParams()->get('reset_time');
  336. $this->setError(JText::plural('COM_USERS_REMIND_LIMIT_ERROR_N_HOURS', $resetLimit));
  337. return false;
  338. }
  339. // Set the confirmation token.
  340. $token = JApplication::getHash(JUserHelper::genRandomPassword());
  341. $salt = JUserHelper::getSalt('crypt-md5');
  342. $hashedToken = md5($token . $salt) . ':' . $salt;
  343. $user->activation = $hashedToken;
  344. // Save the user to the database.
  345. if (!$user->save(true))
  346. {
  347. return new JException(JText::sprintf('COM_USERS_USER_SAVE_FAILED', $user->getError()), 500);
  348. }
  349. // Assemble the password reset confirmation link.
  350. $mode = $config->get('force_ssl', 0) == 2 ? 1 : -1;
  351. $itemid = UsersHelperRoute::getLoginRoute();
  352. $itemid = $itemid !== null ? '&Itemid=' . $itemid : '';
  353. $link = 'index.php?option=com_users&view=reset&layout=confirm&token=' . $token . $itemid;
  354. // Put together the email template data.
  355. $data = $user->getProperties();
  356. $data['fromname'] = $config->get('fromname');
  357. $data['mailfrom'] = $config->get('mailfrom');
  358. $data['sitename'] = $config->get('sitename');
  359. $data['link_text'] = JRoute::_($link, false, $mode);
  360. $data['link_html'] = JRoute::_($link, true, $mode);
  361. $data['token'] = $token;
  362. $subject = JText::sprintf(
  363. 'COM_USERS_EMAIL_PASSWORD_RESET_SUBJECT',
  364. $data['sitename']
  365. );
  366. $body = JText::sprintf(
  367. 'COM_USERS_EMAIL_PASSWORD_RESET_BODY',
  368. $data['sitename'],
  369. $data['token'],
  370. $data['link_text']
  371. );
  372. // Send the password reset request email.
  373. $return = JFactory::getMailer()->sendMail($data['mailfrom'], $data['fromname'], $user->email, $subject, $body);
  374. // Check for an error.
  375. if ($return !== true)
  376. {
  377. return new JException(JText::_('COM_USERS_MAIL_FAILED'), 500);
  378. }
  379. return true;
  380. }
  381. /**
  382. * Method to check if user reset limit has been exceeded within the allowed time period.
  383. *
  384. * @param JUser the user doing the password reset
  385. *
  386. * @return boolean true if user can do the reset, false if limit exceeded
  387. *
  388. * @since 2.5
  389. */
  390. public function checkResetLimit($user)
  391. {
  392. $params = JFactory::getApplication()->getParams();
  393. $maxCount = (int) $params->get('reset_count');
  394. $resetHours = (int) $params->get('reset_time');
  395. $result = true;
  396. $lastResetTime = strtotime($user->lastResetTime) ? strtotime($user->lastResetTime) : 0;
  397. $hoursSinceLastReset = (strtotime(JFactory::getDate()->toSql()) - $lastResetTime) / 3600;
  398. if ($hoursSinceLastReset > $resetHours)
  399. {
  400. // If it's been long enough, start a new reset count
  401. $user->lastResetTime = JFactory::getDate()->toSql();
  402. $user->resetCount = 1;
  403. }
  404. elseif ($user->resetCount < $maxCount)
  405. {
  406. // If we are under the max count, just increment the counter
  407. $user->resetCount;
  408. }
  409. else
  410. {
  411. // At this point, we know we have exceeded the maximum resets for the time period
  412. $result = false;
  413. }
  414. return $result;
  415. }
  416. }