loadForm('com_users.reset_request', 'reset_request', array('control' => 'jform', 'load_data' => $loadData)); if (empty($form)) { return false; } return $form; } /** * Method to get the password reset complete form. * * @param array $data Data for the form. * @param boolean $loadData True if the form is to load its own data (default case), false if not. * @return JForm A JForm object on success, false on failure * @since 1.6 */ public function getResetCompleteForm($data = array(), $loadData = true) { // Get the form. $form = $this->loadForm('com_users.reset_complete', 'reset_complete', $options = array('control' => 'jform')); if (empty($form)) { return false; } return $form; } /** * Method to get the password reset confirm form. * * @param array $data Data for the form. * @param boolean $loadData True if the form is to load its own data (default case), false if not. * @return JForm A JForm object on success, false on failure * @since 1.6 */ public function getResetConfirmForm($data = array(), $loadData = true) { // Get the form. $form = $this->loadForm('com_users.reset_confirm', 'reset_confirm', $options = array('control' => 'jform')); if (empty($form)) { return false; } return $form; } /** * Override preprocessForm to load the user plugin group instead of content. * * @param object A form object. * @param mixed The data expected for the form. * @throws Exception if there is an error in the form event. * @since 1.6 */ protected function preprocessForm(JForm $form, $data, $group = 'user') { parent::preprocessForm($form, $data, $group); } /** * Method to auto-populate the model state. * * Note. Calling getState in this method will result in recursion. * * @since 1.6 */ protected function populateState() { // Get the application object. $params = JFactory::getApplication()->getParams('com_users'); // Load the parameters. $this->setState('params', $params); } /** * @since 1.6 */ function processResetComplete($data) { // Get the form. $form = $this->getResetCompleteForm(); $data['email'] = JStringPunycode::emailToPunycode($data['email']); // Check for an error. if ($form instanceof Exception) { return $form; } // Filter and validate the form data. $data = $form->filter($data); $return = $form->validate($data); // Check for an error. if ($return instanceof Exception) { return $return; } // Check the validation results. if ($return === false) { // Get the validation messages from the form. foreach ($form->getErrors() as $formError) { $this->setError($formError->getMessage()); } return false; } // Get the token and user id from the confirmation process. $app = JFactory::getApplication(); $token = $app->getUserState('com_users.reset.token', null); $userId = $app->getUserState('com_users.reset.user', null); // Check the token and user id. if (empty($token) || empty($userId)) { return new JException(JText::_('COM_USERS_RESET_COMPLETE_TOKENS_MISSING'), 403); } // Get the user object. $user = JUser::getInstance($userId); // Check for a user and that the tokens match. if (empty($user) || $user->activation !== $token) { $this->setError(JText::_('COM_USERS_USER_NOT_FOUND')); return false; } // Make sure the user isn't blocked. if ($user->block) { $this->setError(JText::_('COM_USERS_USER_BLOCKED')); return false; } // Generate the new password hash. $salt = JUserHelper::genRandomPassword(32); $crypted = JUserHelper::getCryptedPassword($data['password1'], $salt); $password = $crypted . ':' . $salt; // Update the user object. $user->password = $password; $user->activation = ''; $user->password_clear = $data['password1']; // Save the user to the database. if (!$user->save(true)) { return new JException(JText::sprintf('COM_USERS_USER_SAVE_FAILED', $user->getError()), 500); } // Flush the user data from the session. $app->setUserState('com_users.reset.token', null); $app->setUserState('com_users.reset.user', null); return true; } /** * @since 1.6 */ function processResetConfirm($data) { // Get the form. $form = $this->getResetConfirmForm(); $data['email'] = JStringPunycode::emailToPunycode($data['email']); // Check for an error. if ($form instanceof Exception) { return $form; } // Filter and validate the form data. $data = $form->filter($data); $return = $form->validate($data); // Check for an error. if ($return instanceof Exception) { return $return; } // Check the validation results. if ($return === false) { // Get the validation messages from the form. foreach ($form->getErrors() as $formError) { $this->setError($formError->getMessage()); } return false; } // Find the user id for the given token. $db = $this->getDbo(); $query = $db->getQuery(true) ->select('activation') ->select('id') ->select('block') ->from($db->quoteName('#__users')) ->where($db->quoteName('username') . ' = ' . $db->quote($data['username'])); // Get the user id. $db->setQuery($query); try { $user = $db->loadObject(); } catch (RuntimeException $e) { return new JException(JText::sprintf('COM_USERS_DATABASE_ERROR', $e->getMessage()), 500); } // Check for a user. if (empty($user)) { $this->setError(JText::_('COM_USERS_USER_NOT_FOUND')); return false; } $parts = explode(':', $user->activation); $crypt = $parts[0]; if (!isset($parts[1])) { $this->setError(JText::_('COM_USERS_USER_NOT_FOUND')); return false; } $salt = $parts[1]; $testcrypt = JUserHelper::getCryptedPassword($data['token'], $salt); // Verify the token if (!($crypt == $testcrypt)) { $this->setError(JText::_('COM_USERS_USER_NOT_FOUND')); return false; } // Make sure the user isn't blocked. if ($user->block) { $this->setError(JText::_('COM_USERS_USER_BLOCKED')); return false; } // Push the user data into the session. $app = JFactory::getApplication(); $app->setUserState('com_users.reset.token', $crypt . ':' . $salt); $app->setUserState('com_users.reset.user', $user->id); return true; } /** * Method to start the password reset process. * * @since 1.6 */ public function processResetRequest($data) { $config = JFactory::getConfig(); // Get the form. $form = $this->getForm(); $data['email'] = JStringPunycode::emailToPunycode($data['email']); // Check for an error. if ($form instanceof Exception) { return $form; } // Filter and validate the form data. $data = $form->filter($data); $return = $form->validate($data); // Check for an error. if ($return instanceof Exception) { return $return; } // Check the validation results. if ($return === false) { // Get the validation messages from the form. foreach ($form->getErrors() as $formError) { $this->setError($formError->getMessage()); } return false; } // Find the user id for the given email address. $db = $this->getDbo(); $query = $db->getQuery(true) ->select('id') ->from($db->quoteName('#__users')) ->where($db->quoteName('email') . ' = ' . $db->quote($data['email'])); // Get the user object. $db->setQuery($query); try { $userId = $db->loadResult(); } catch (RuntimeException $e) { $this->setError(JText::sprintf('COM_USERS_DATABASE_ERROR', $e->getMessage()), 500); return false; } // Check for a user. if (empty($userId)) { $this->setError(JText::_('COM_USERS_INVALID_EMAIL')); return false; } // Get the user object. $user = JUser::getInstance($userId); // Make sure the user isn't blocked. if ($user->block) { $this->setError(JText::_('COM_USERS_USER_BLOCKED')); return false; } // Make sure the user isn't a Super Admin. if ($user->authorise('core.admin')) { $this->setError(JText::_('COM_USERS_REMIND_SUPERADMIN_ERROR')); return false; } // Make sure the user has not exceeded the reset limit if (!$this->checkResetLimit($user)) { $resetLimit = (int) JFactory::getApplication()->getParams()->get('reset_time'); $this->setError(JText::plural('COM_USERS_REMIND_LIMIT_ERROR_N_HOURS', $resetLimit)); return false; } // Set the confirmation token. $token = JApplication::getHash(JUserHelper::genRandomPassword()); $salt = JUserHelper::getSalt('crypt-md5'); $hashedToken = md5($token . $salt) . ':' . $salt; $user->activation = $hashedToken; // Save the user to the database. if (!$user->save(true)) { return new JException(JText::sprintf('COM_USERS_USER_SAVE_FAILED', $user->getError()), 500); } // Assemble the password reset confirmation link. $mode = $config->get('force_ssl', 0) == 2 ? 1 : -1; $itemid = UsersHelperRoute::getLoginRoute(); $itemid = $itemid !== null ? '&Itemid=' . $itemid : ''; $link = 'index.php?option=com_users&view=reset&layout=confirm' . $itemid; // Put together the email template data. $data = $user->getProperties(); $data['fromname'] = $config->get('fromname'); $data['mailfrom'] = $config->get('mailfrom'); $data['sitename'] = $config->get('sitename'); $data['link_text'] = JRoute::_($link, false, $mode); $data['link_html'] = JRoute::_($link, true, $mode); $data['token'] = $token; $subject = JText::sprintf( 'COM_USERS_EMAIL_PASSWORD_RESET_SUBJECT', $data['sitename'] ); $body = JText::sprintf( 'COM_USERS_EMAIL_PASSWORD_RESET_BODY', $data['sitename'], $data['token'], $data['link_text'] ); // Send the password reset request email. $return = JFactory::getMailer()->sendMail($data['mailfrom'], $data['fromname'], $user->email, $subject, $body); // Check for an error. if ($return !== true) { return new JException(JText::_('COM_USERS_MAIL_FAILED'), 500); } return true; } /** * Method to check if user reset limit has been exceeded within the allowed time period. * * @param JUser the user doing the password reset * * @return boolean true if user can do the reset, false if limit exceeded * * @since 2.5 */ public function checkResetLimit($user) { $params = JFactory::getApplication()->getParams(); $maxCount = (int) $params->get('reset_count'); $resetHours = (int) $params->get('reset_time'); $result = true; $lastResetTime = strtotime($user->lastResetTime) ? strtotime($user->lastResetTime) : 0; $hoursSinceLastReset = (strtotime(JFactory::getDate()->toSql()) - $lastResetTime) / 3600; // If it's been long enough, start a new reset count if ($hoursSinceLastReset > $resetHours) { $user->lastResetTime = JFactory::getDate()->toSql(); $user->resetCount = 1; } // If we are under the max count, just increment the counter elseif ($user->resetCount < $maxCount) { $user->resetCount; } // At this point, we know we have exceeded the maximum resets for the time period else { $result = false; } return $result; } }