PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/components/com_users/models/reset.php

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