PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/components/com_users/models/reset.php

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