[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Site 5 * @subpackage com_users 6 * 7 * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> 8 * @license GNU General Public License version 2 or later; see LICENSE.txt 9 */ 10 11 namespace Joomla\Component\Users\Site\Model; 12 13 use Joomla\CMS\Application\ApplicationHelper; 14 use Joomla\CMS\Factory; 15 use Joomla\CMS\Form\Form; 16 use Joomla\CMS\Language\Text; 17 use Joomla\CMS\Log\Log; 18 use Joomla\CMS\Mail\MailTemplate; 19 use Joomla\CMS\MVC\Model\FormModel; 20 use Joomla\CMS\Router\Route; 21 use Joomla\CMS\String\PunycodeHelper; 22 use Joomla\CMS\User\User; 23 use Joomla\CMS\User\UserHelper; 24 25 // phpcs:disable PSR1.Files.SideEffects 26 \defined('_JEXEC') or die; 27 // phpcs:enable PSR1.Files.SideEffects 28 29 /** 30 * Reset model class for Users. 31 * 32 * @since 1.5 33 */ 34 class ResetModel extends FormModel 35 { 36 /** 37 * Method to get the password reset request form. 38 * 39 * The base form is loaded from XML and then an event is fired 40 * for users plugins to extend the form with extra fields. 41 * 42 * @param array $data An optional array of data for the form to interrogate. 43 * @param boolean $loadData True if the form is to load its own data (default case), false if not. 44 * 45 * @return Form A Form object on success, false on failure 46 * 47 * @since 1.6 48 */ 49 public function getForm($data = array(), $loadData = true) 50 { 51 // Get the form. 52 $form = $this->loadForm('com_users.reset_request', 'reset_request', array('control' => 'jform', 'load_data' => $loadData)); 53 54 if (empty($form)) { 55 return false; 56 } 57 58 return $form; 59 } 60 61 /** 62 * Method to get the password reset complete form. 63 * 64 * @param array $data Data for the form. 65 * @param boolean $loadData True if the form is to load its own data (default case), false if not. 66 * 67 * @return Form A Form object on success, false on failure 68 * 69 * @since 1.6 70 */ 71 public function getResetCompleteForm($data = array(), $loadData = true) 72 { 73 // Get the form. 74 $form = $this->loadForm('com_users.reset_complete', 'reset_complete', $options = array('control' => 'jform')); 75 76 if (empty($form)) { 77 return false; 78 } 79 80 return $form; 81 } 82 83 /** 84 * Method to get the password reset confirm form. 85 * 86 * @param array $data Data for the form. 87 * @param boolean $loadData True if the form is to load its own data (default case), false if not. 88 * 89 * @return Form A Form object on success, false on failure 90 * 91 * @since 1.6 92 * @throws \Exception 93 */ 94 public function getResetConfirmForm($data = array(), $loadData = true) 95 { 96 // Get the form. 97 $form = $this->loadForm('com_users.reset_confirm', 'reset_confirm', $options = array('control' => 'jform')); 98 99 if (empty($form)) { 100 return false; 101 } else { 102 $form->setValue('token', '', Factory::getApplication()->input->get('token')); 103 } 104 105 return $form; 106 } 107 108 /** 109 * Override preprocessForm to load the user plugin group instead of content. 110 * 111 * @param Form $form A Form object. 112 * @param mixed $data The data expected for the form. 113 * @param string $group The name of the plugin group to import (defaults to "content"). 114 * 115 * @return void 116 * 117 * @throws \Exception if there is an error in the form event. 118 * 119 * @since 1.6 120 */ 121 protected function preprocessForm(Form $form, $data, $group = 'user') 122 { 123 parent::preprocessForm($form, $data, $group); 124 } 125 126 /** 127 * Method to auto-populate the model state. 128 * 129 * Note. Calling getState in this method will result in recursion. 130 * 131 * @return void 132 * 133 * @since 1.6 134 * @throws \Exception 135 */ 136 protected function populateState() 137 { 138 // Get the application object. 139 $params = Factory::getApplication()->getParams('com_users'); 140 141 // Load the parameters. 142 $this->setState('params', $params); 143 } 144 145 /** 146 * Save the new password after reset is done 147 * 148 * @param array $data The data expected for the form. 149 * 150 * @return mixed \Exception | boolean 151 * 152 * @since 1.6 153 * @throws \Exception 154 */ 155 public function processResetComplete($data) 156 { 157 // Get the form. 158 $form = $this->getResetCompleteForm(); 159 160 // Check for an error. 161 if ($form instanceof \Exception) { 162 return $form; 163 } 164 165 // Filter and validate the form data. 166 $data = $form->filter($data); 167 $return = $form->validate($data); 168 169 // Check for an error. 170 if ($return instanceof \Exception) { 171 return $return; 172 } 173 174 // Check the validation results. 175 if ($return === false) { 176 // Get the validation messages from the form. 177 foreach ($form->getErrors() as $formError) { 178 $this->setError($formError->getMessage()); 179 } 180 181 return false; 182 } 183 184 // Get the token and user id from the confirmation process. 185 $app = Factory::getApplication(); 186 $token = $app->getUserState('com_users.reset.token', null); 187 $userId = $app->getUserState('com_users.reset.user', null); 188 189 // Check the token and user id. 190 if (empty($token) || empty($userId)) { 191 return new \Exception(Text::_('COM_USERS_RESET_COMPLETE_TOKENS_MISSING'), 403); 192 } 193 194 // Get the user object. 195 $user = User::getInstance($userId); 196 197 // Check for a user and that the tokens match. 198 if (empty($user) || $user->activation !== $token) { 199 $this->setError(Text::_('COM_USERS_USER_NOT_FOUND')); 200 201 return false; 202 } 203 204 // Make sure the user isn't blocked. 205 if ($user->block) { 206 $this->setError(Text::_('COM_USERS_USER_BLOCKED')); 207 208 return false; 209 } 210 211 // Check if the user is reusing the current password if required to reset their password 212 if ($user->requireReset == 1 && UserHelper::verifyPassword($data['password1'], $user->password)) { 213 $this->setError(Text::_('JLIB_USER_ERROR_CANNOT_REUSE_PASSWORD')); 214 215 return false; 216 } 217 218 // Prepare user data. 219 $data['password'] = $data['password1']; 220 $data['activation'] = ''; 221 222 // Update the user object. 223 if (!$user->bind($data)) { 224 return new \Exception($user->getError(), 500); 225 } 226 227 // Save the user to the database. 228 if (!$user->save(true)) { 229 return new \Exception(Text::sprintf('COM_USERS_USER_SAVE_FAILED', $user->getError()), 500); 230 } 231 232 // Destroy all active sessions for the user 233 UserHelper::destroyUserSessions($user->id); 234 235 // Flush the user data from the session. 236 $app->setUserState('com_users.reset.token', null); 237 $app->setUserState('com_users.reset.user', null); 238 239 return true; 240 } 241 242 /** 243 * Receive the reset password request 244 * 245 * @param array $data The data expected for the form. 246 * 247 * @return mixed \Exception | boolean 248 * 249 * @since 1.6 250 * @throws \Exception 251 */ 252 public function processResetConfirm($data) 253 { 254 // Get the form. 255 $form = $this->getResetConfirmForm(); 256 257 // Check for an error. 258 if ($form instanceof \Exception) { 259 return $form; 260 } 261 262 // Filter and validate the form data. 263 $data = $form->filter($data); 264 $return = $form->validate($data); 265 266 // Check for an error. 267 if ($return instanceof \Exception) { 268 return $return; 269 } 270 271 // Check the validation results. 272 if ($return === false) { 273 // Get the validation messages from the form. 274 foreach ($form->getErrors() as $formError) { 275 $this->setError($formError->getMessage()); 276 } 277 278 return false; 279 } 280 281 // Find the user id for the given token. 282 $db = $this->getDatabase(); 283 $query = $db->getQuery(true) 284 ->select($db->quoteName(['activation', 'id', 'block'])) 285 ->from($db->quoteName('#__users')) 286 ->where($db->quoteName('username') . ' = :username') 287 ->bind(':username', $data['username']); 288 289 // Get the user id. 290 $db->setQuery($query); 291 292 try { 293 $user = $db->loadObject(); 294 } catch (\RuntimeException $e) { 295 return new \Exception(Text::sprintf('COM_USERS_DATABASE_ERROR', $e->getMessage()), 500); 296 } 297 298 // Check for a user. 299 if (empty($user)) { 300 $this->setError(Text::_('COM_USERS_USER_NOT_FOUND')); 301 302 return false; 303 } 304 305 if (!$user->activation) { 306 $this->setError(Text::_('COM_USERS_USER_NOT_FOUND')); 307 308 return false; 309 } 310 311 // Verify the token 312 if (!UserHelper::verifyPassword($data['token'], $user->activation)) { 313 $this->setError(Text::_('COM_USERS_USER_NOT_FOUND')); 314 315 return false; 316 } 317 318 // Make sure the user isn't blocked. 319 if ($user->block) { 320 $this->setError(Text::_('COM_USERS_USER_BLOCKED')); 321 322 return false; 323 } 324 325 // Push the user data into the session. 326 $app = Factory::getApplication(); 327 $app->setUserState('com_users.reset.token', $user->activation); 328 $app->setUserState('com_users.reset.user', $user->id); 329 330 return true; 331 } 332 333 /** 334 * Method to start the password reset process. 335 * 336 * @param array $data The data expected for the form. 337 * 338 * @return mixed \Exception | boolean 339 * 340 * @since 1.6 341 * @throws \Exception 342 */ 343 public function processResetRequest($data) 344 { 345 $app = Factory::getApplication(); 346 347 // Get the form. 348 $form = $this->getForm(); 349 350 $data['email'] = PunycodeHelper::emailToPunycode($data['email']); 351 352 // Check for an error. 353 if ($form instanceof \Exception) { 354 return $form; 355 } 356 357 // Filter and validate the form data. 358 $data = $form->filter($data); 359 $return = $form->validate($data); 360 361 // Check for an error. 362 if ($return instanceof \Exception) { 363 return $return; 364 } 365 366 // Check the validation results. 367 if ($return === false) { 368 // Get the validation messages from the form. 369 foreach ($form->getErrors() as $formError) { 370 $this->setError($formError->getMessage()); 371 } 372 373 return false; 374 } 375 376 // Find the user id for the given email address. 377 $db = $this->getDatabase(); 378 $query = $db->getQuery(true) 379 ->select($db->quoteName('id')) 380 ->from($db->quoteName('#__users')) 381 ->where('LOWER(' . $db->quoteName('email') . ') = LOWER(:email)') 382 ->bind(':email', $data['email']); 383 384 // Get the user object. 385 $db->setQuery($query); 386 387 try { 388 $userId = $db->loadResult(); 389 } catch (\RuntimeException $e) { 390 $this->setError(Text::sprintf('COM_USERS_DATABASE_ERROR', $e->getMessage())); 391 392 return false; 393 } 394 395 // Check for a user. 396 if (empty($userId)) { 397 $this->setError(Text::_('COM_USERS_INVALID_EMAIL')); 398 399 return false; 400 } 401 402 // Get the user object. 403 $user = User::getInstance($userId); 404 405 // Make sure the user isn't blocked. 406 if ($user->block) { 407 $this->setError(Text::_('COM_USERS_USER_BLOCKED')); 408 409 return false; 410 } 411 412 // Make sure the user isn't a Super Admin. 413 if ($user->authorise('core.admin')) { 414 $this->setError(Text::_('COM_USERS_REMIND_SUPERADMIN_ERROR')); 415 416 return false; 417 } 418 419 // Make sure the user has not exceeded the reset limit 420 if (!$this->checkResetLimit($user)) { 421 $resetLimit = (int) Factory::getApplication()->getParams()->get('reset_time'); 422 $this->setError(Text::plural('COM_USERS_REMIND_LIMIT_ERROR_N_HOURS', $resetLimit)); 423 424 return false; 425 } 426 427 // Set the confirmation token. 428 $token = ApplicationHelper::getHash(UserHelper::genRandomPassword()); 429 $hashedToken = UserHelper::hashPassword($token); 430 431 $user->activation = $hashedToken; 432 433 // Save the user to the database. 434 if (!$user->save(true)) { 435 return new \Exception(Text::sprintf('COM_USERS_USER_SAVE_FAILED', $user->getError()), 500); 436 } 437 438 // Assemble the password reset confirmation link. 439 $mode = $app->get('force_ssl', 0) == 2 ? 1 : (-1); 440 $link = 'index.php?option=com_users&view=reset&layout=confirm&token=' . $token; 441 442 // Put together the email template data. 443 $data = $user->getProperties(); 444 $data['sitename'] = $app->get('sitename'); 445 $data['link_text'] = Route::_($link, false, $mode); 446 $data['link_html'] = Route::_($link, true, $mode); 447 $data['token'] = $token; 448 449 $mailer = new MailTemplate('com_users.password_reset', $app->getLanguage()->getTag()); 450 $mailer->addTemplateData($data); 451 $mailer->addRecipient($user->email, $user->name); 452 453 // Try to send the password reset request email. 454 try { 455 $return = $mailer->send(); 456 } catch (\Exception $exception) { 457 try { 458 Log::add(Text::_($exception->getMessage()), Log::WARNING, 'jerror'); 459 460 $return = false; 461 } catch (\RuntimeException $exception) { 462 Factory::getApplication()->enqueueMessage(Text::_($exception->errorMessage()), 'warning'); 463 464 $return = false; 465 } 466 } 467 468 // Check for an error. 469 if ($return !== true) { 470 return new \Exception(Text::_('COM_USERS_MAIL_FAILED'), 500); 471 } else { 472 return true; 473 } 474 } 475 476 /** 477 * Method to check if user reset limit has been exceeded within the allowed time period. 478 * 479 * @param User $user User doing the password reset 480 * 481 * @return boolean true if user can do the reset, false if limit exceeded 482 * 483 * @since 2.5 484 * @throws \Exception 485 */ 486 public function checkResetLimit($user) 487 { 488 $params = Factory::getApplication()->getParams(); 489 $maxCount = (int) $params->get('reset_count'); 490 $resetHours = (int) $params->get('reset_time'); 491 $result = true; 492 493 $lastResetTime = strtotime($user->lastResetTime) ?: 0; 494 $hoursSinceLastReset = (strtotime(Factory::getDate()->toSql()) - $lastResetTime) / 3600; 495 496 if ($hoursSinceLastReset > $resetHours) { 497 // If it's been long enough, start a new reset count 498 $user->lastResetTime = Factory::getDate()->toSql(); 499 $user->resetCount = 1; 500 } elseif ($user->resetCount < $maxCount) { 501 // If we are under the max count, just increment the counter 502 ++$user->resetCount; 503 } else { 504 // At this point, we know we have exceeded the maximum resets for the time period 505 $result = false; 506 } 507 508 return $result; 509 } 510 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |