[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Joomla! Content Management System 5 * 6 * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10 namespace Joomla\CMS\User; 11 12 use Joomla\CMS\Access\Access; 13 use Joomla\CMS\Factory; 14 use Joomla\CMS\Language\Text; 15 use Joomla\CMS\Log\Log; 16 use Joomla\CMS\Object\CMSObject; 17 use Joomla\CMS\Plugin\PluginHelper; 18 use Joomla\CMS\Table\Table; 19 use Joomla\Registry\Registry; 20 use Joomla\Utilities\ArrayHelper; 21 22 // phpcs:disable PSR1.Files.SideEffects 23 \defined('JPATH_PLATFORM') or die; 24 // phpcs:enable PSR1.Files.SideEffects 25 26 /** 27 * User class. Handles all application interaction with a user 28 * 29 * @since 1.7.0 30 */ 31 class User extends CMSObject 32 { 33 /** 34 * A cached switch for if this user has root access rights. 35 * 36 * @var boolean 37 * @since 1.7.0 38 */ 39 protected $isRoot = null; 40 41 /** 42 * Unique id 43 * 44 * @var integer 45 * @since 1.7.0 46 */ 47 public $id = null; 48 49 /** 50 * The user's real name (or nickname) 51 * 52 * @var string 53 * @since 1.7.0 54 */ 55 public $name = null; 56 57 /** 58 * The login name 59 * 60 * @var string 61 * @since 1.7.0 62 */ 63 public $username = null; 64 65 /** 66 * The email 67 * 68 * @var string 69 * @since 1.7.0 70 */ 71 public $email = null; 72 73 /** 74 * MD5 encrypted password 75 * 76 * @var string 77 * @since 1.7.0 78 */ 79 public $password = null; 80 81 /** 82 * Clear password, only available when a new password is set for a user 83 * 84 * @var string 85 * @since 1.7.0 86 */ 87 public $password_clear = ''; 88 89 /** 90 * Block status 91 * 92 * @var integer 93 * @since 1.7.0 94 */ 95 public $block = null; 96 97 /** 98 * Should this user receive system email 99 * 100 * @var integer 101 * @since 1.7.0 102 */ 103 public $sendEmail = null; 104 105 /** 106 * Date the user was registered 107 * 108 * @var string 109 * @since 1.7.0 110 */ 111 public $registerDate = null; 112 113 /** 114 * Date of last visit 115 * 116 * @var string 117 * @since 1.7.0 118 */ 119 public $lastvisitDate = null; 120 121 /** 122 * Activation hash 123 * 124 * @var string 125 * @since 1.7.0 126 */ 127 public $activation = null; 128 129 /** 130 * User parameters 131 * 132 * @var Registry 133 * @since 1.7.0 134 */ 135 public $params = null; 136 137 /** 138 * Associative array of user names => group ids 139 * 140 * @var array 141 * @since 1.7.0 142 */ 143 public $groups = array(); 144 145 /** 146 * Guest status 147 * 148 * @var integer 149 * @since 1.7.0 150 */ 151 public $guest = null; 152 153 /** 154 * Last Reset Time 155 * 156 * @var string 157 * @since 3.0.1 158 */ 159 public $lastResetTime = null; 160 161 /** 162 * Count since last Reset Time 163 * 164 * @var integer 165 * @since 3.0.1 166 */ 167 public $resetCount = null; 168 169 /** 170 * Flag to require the user's password be reset 171 * 172 * @var integer 173 * @since 3.2 174 */ 175 public $requireReset = null; 176 177 /** 178 * User parameters 179 * 180 * @var Registry 181 * @since 1.7.0 182 */ 183 protected $_params = null; 184 185 /** 186 * Authorised access groups 187 * 188 * @var array 189 * @since 1.7.0 190 */ 191 protected $_authGroups = null; 192 193 /** 194 * Authorised access levels 195 * 196 * @var array 197 * @since 1.7.0 198 */ 199 protected $_authLevels = null; 200 201 /** 202 * Authorised access actions 203 * 204 * @var array 205 * @since 1.7.0 206 */ 207 protected $_authActions = null; 208 209 /** 210 * Error message 211 * 212 * @var string 213 * @since 1.7.0 214 */ 215 protected $_errorMsg = null; 216 217 /** 218 * @var array User instances container. 219 * @since 1.7.3 220 */ 221 protected static $instances = array(); 222 223 /** 224 * Constructor activating the default information of the language 225 * 226 * @param integer $identifier The primary key of the user to load (optional). 227 * 228 * @since 1.7.0 229 */ 230 public function __construct($identifier = 0) 231 { 232 // Create the user parameters object 233 $this->_params = new Registry(); 234 235 // Load the user if it exists 236 if (!empty($identifier)) { 237 $this->load($identifier); 238 } else { 239 // Initialise 240 $this->id = 0; 241 $this->sendEmail = 0; 242 $this->aid = 0; 243 $this->guest = 1; 244 } 245 } 246 247 /** 248 * Returns the global User object, only creating it if it doesn't already exist. 249 * 250 * @param integer $identifier The primary key of the user to load (optional). 251 * 252 * @return User The User object. 253 * 254 * @since 1.7.0 255 * @deprecated 5.0 Load the user service from the dependency injection container or via $app->getIdentity() 256 */ 257 public static function getInstance($identifier = 0) 258 { 259 @trigger_error( 260 sprintf( 261 '%1$s() is deprecated. Load the user from the dependency injection container or via %2$s::getApplication()->getIdentity().', 262 __METHOD__, 263 __CLASS__ 264 ), 265 E_USER_DEPRECATED 266 ); 267 268 // Find the user id 269 if (!is_numeric($identifier)) { 270 return Factory::getContainer()->get(UserFactoryInterface::class)->loadUserByUsername($identifier); 271 } else { 272 $id = $identifier; 273 } 274 275 // If the $id is zero, just return an empty User. 276 // Note: don't cache this user because it'll have a new ID on save! 277 if ($id === 0) { 278 return Factory::getContainer()->get(UserFactoryInterface::class)->loadUserById($id); 279 } 280 281 // Check if the user ID is already cached. 282 if (empty(self::$instances[$id])) { 283 self::$instances[$id] = Factory::getContainer()->get(UserFactoryInterface::class)->loadUserById($id); 284 } 285 286 return self::$instances[$id]; 287 } 288 289 /** 290 * Method to get a parameter value 291 * 292 * @param string $key Parameter key 293 * @param mixed $default Parameter default value 294 * 295 * @return mixed The value or the default if it did not exist 296 * 297 * @since 1.7.0 298 */ 299 public function getParam($key, $default = null) 300 { 301 return $this->_params->get($key, $default); 302 } 303 304 /** 305 * Method to set a parameter 306 * 307 * @param string $key Parameter key 308 * @param mixed $value Parameter value 309 * 310 * @return mixed Set parameter value 311 * 312 * @since 1.7.0 313 */ 314 public function setParam($key, $value) 315 { 316 return $this->_params->set($key, $value); 317 } 318 319 /** 320 * Method to set a default parameter if it does not exist 321 * 322 * @param string $key Parameter key 323 * @param mixed $value Parameter value 324 * 325 * @return mixed Set parameter value 326 * 327 * @since 1.7.0 328 */ 329 public function defParam($key, $value) 330 { 331 return $this->_params->def($key, $value); 332 } 333 334 /** 335 * Method to check User object authorisation against an access control 336 * object and optionally an access extension object 337 * 338 * @param string $action The name of the action to check for permission. 339 * @param string $assetname The name of the asset on which to perform the action. 340 * 341 * @return boolean True if authorised 342 * 343 * @since 1.7.0 344 */ 345 public function authorise($action, $assetname = null) 346 { 347 // Make sure we only check for core.admin once during the run. 348 if ($this->isRoot === null) { 349 $this->isRoot = false; 350 351 // Check for the configuration file failsafe. 352 $rootUser = Factory::getApplication()->get('root_user'); 353 354 // The root_user variable can be a numeric user ID or a username. 355 if (is_numeric($rootUser) && $this->id > 0 && $this->id == $rootUser) { 356 $this->isRoot = true; 357 } elseif ($this->username && $this->username == $rootUser) { 358 $this->isRoot = true; 359 } elseif ($this->id > 0) { 360 // Get all groups against which the user is mapped. 361 $identities = $this->getAuthorisedGroups(); 362 array_unshift($identities, $this->id * -1); 363 364 if (Access::getAssetRules(1)->allow('core.admin', $identities)) { 365 $this->isRoot = true; 366 367 return true; 368 } 369 } 370 } 371 372 return $this->isRoot ? true : (bool) Access::check($this->id, $action, $assetname); 373 } 374 375 /** 376 * Method to return a list of all categories that a user has permission for a given action 377 * 378 * @param string $component The component from which to retrieve the categories 379 * @param string $action The name of the section within the component from which to retrieve the actions. 380 * 381 * @return array List of categories that this group can do this action to (empty array if none). Categories must be published. 382 * 383 * @since 1.7.0 384 */ 385 public function getAuthorisedCategories($component, $action) 386 { 387 // Brute force method: get all published category rows for the component and check each one 388 // @todo: Modify the way permissions are stored in the db to allow for faster implementation and better scaling 389 $db = Factory::getDbo(); 390 391 $subQuery = $db->getQuery(true) 392 ->select($db->quoteName(['id', 'asset_id'])) 393 ->from($db->quoteName('#__categories')) 394 ->where( 395 [ 396 $db->quoteName('extension') . ' = :component', 397 $db->quoteName('published') . ' = 1', 398 ] 399 ); 400 401 $query = $db->getQuery(true) 402 ->select($db->quoteName(['c.id', 'a.name'])) 403 ->from('(' . $subQuery . ') AS ' . $db->quoteName('c')) 404 ->join('INNER', $db->quoteName('#__assets', 'a'), $db->quoteName('c.asset_id') . ' = ' . $db->quoteName('a.id')) 405 ->bind(':component', $component); 406 $db->setQuery($query); 407 $allCategories = $db->loadObjectList('id'); 408 $allowedCategories = array(); 409 410 foreach ($allCategories as $category) { 411 if ($this->authorise($action, $category->name)) { 412 $allowedCategories[] = (int) $category->id; 413 } 414 } 415 416 return $allowedCategories; 417 } 418 419 /** 420 * Gets an array of the authorised access levels for the user 421 * 422 * @return array 423 * 424 * @since 1.7.0 425 */ 426 public function getAuthorisedViewLevels() 427 { 428 if ($this->_authLevels === null) { 429 $this->_authLevels = array(); 430 } 431 432 if (empty($this->_authLevels)) { 433 $this->_authLevels = Access::getAuthorisedViewLevels($this->id); 434 } 435 436 return $this->_authLevels; 437 } 438 439 /** 440 * Gets an array of the authorised user groups 441 * 442 * @return array 443 * 444 * @since 1.7.0 445 */ 446 public function getAuthorisedGroups() 447 { 448 if ($this->_authGroups === null) { 449 $this->_authGroups = array(); 450 } 451 452 if (empty($this->_authGroups)) { 453 $this->_authGroups = Access::getGroupsByUser($this->id); 454 } 455 456 return $this->_authGroups; 457 } 458 459 /** 460 * Clears the access rights cache of this user 461 * 462 * @return void 463 * 464 * @since 3.4.0 465 */ 466 public function clearAccessRights() 467 { 468 $this->_authLevels = null; 469 $this->_authGroups = null; 470 $this->isRoot = null; 471 Access::clearStatics(); 472 } 473 474 /** 475 * Pass through method to the table for setting the last visit date 476 * 477 * @param integer $timestamp The timestamp, defaults to 'now'. 478 * 479 * @return boolean True on success. 480 * 481 * @since 1.7.0 482 */ 483 public function setLastVisit($timestamp = null) 484 { 485 // Create the user table object 486 /** @var \Joomla\CMS\Table\User $table */ 487 $table = $this->getTable(); 488 $table->load($this->id); 489 490 return $table->setLastVisit($timestamp); 491 } 492 493 /** 494 * Method to get the user timezone. 495 * 496 * If the user didn't set a timezone, it will return the server timezone 497 * 498 * @return \DateTimeZone 499 * 500 * @since 3.7.0 501 */ 502 public function getTimezone() 503 { 504 $timezone = $this->getParam('timezone', Factory::getApplication()->get('offset', 'GMT')); 505 506 return new \DateTimeZone($timezone); 507 } 508 509 /** 510 * Method to get the user parameters 511 * 512 * @param object $params The user parameters object 513 * 514 * @return void 515 * 516 * @since 1.7.0 517 */ 518 public function setParameters($params) 519 { 520 $this->_params = $params; 521 } 522 523 /** 524 * Method to get the user table object 525 * 526 * This function uses a static variable to store the table name of the user table to 527 * instantiate. You can call this function statically to set the table name if 528 * needed. 529 * 530 * @param string $type The user table name to be used 531 * @param string $prefix The user table prefix to be used 532 * 533 * @return Table The user table object 534 * 535 * @note At 4.0 this method will no longer be static 536 * @since 1.7.0 537 */ 538 public static function getTable($type = null, $prefix = 'JTable') 539 { 540 static $tabletype; 541 542 // Set the default tabletype; 543 if (!isset($tabletype)) { 544 $tabletype['name'] = 'user'; 545 $tabletype['prefix'] = 'JTable'; 546 } 547 548 // Set a custom table type is defined 549 if (isset($type)) { 550 $tabletype['name'] = $type; 551 $tabletype['prefix'] = $prefix; 552 } 553 554 // Create the user table object 555 return Table::getInstance($tabletype['name'], $tabletype['prefix']); 556 } 557 558 /** 559 * Method to bind an associative array of data to a user object 560 * 561 * @param array &$array The associative array to bind to the object 562 * 563 * @return boolean True on success 564 * 565 * @since 1.7.0 566 */ 567 public function bind(&$array) 568 { 569 // Let's check to see if the user is new or not 570 if (empty($this->id)) { 571 // Check the password and create the crypted password 572 if (empty($array['password'])) { 573 $array['password'] = UserHelper::genRandomPassword(32); 574 $array['password2'] = $array['password']; 575 } 576 577 // Not all controllers check the password, although they should. 578 // Hence this code is required: 579 if (isset($array['password2']) && $array['password'] != $array['password2']) { 580 Factory::getApplication()->enqueueMessage(Text::_('JLIB_USER_ERROR_PASSWORD_NOT_MATCH'), 'error'); 581 582 return false; 583 } 584 585 $this->password_clear = ArrayHelper::getValue($array, 'password', '', 'string'); 586 587 $array['password'] = UserHelper::hashPassword($array['password']); 588 589 // Set the registration timestamp 590 $this->set('registerDate', Factory::getDate()->toSql()); 591 } else { 592 // Updating an existing user 593 if (!empty($array['password'])) { 594 if ($array['password'] != $array['password2']) { 595 $this->setError(Text::_('JLIB_USER_ERROR_PASSWORD_NOT_MATCH')); 596 597 return false; 598 } 599 600 $this->password_clear = ArrayHelper::getValue($array, 'password', '', 'string'); 601 602 // Check if the user is reusing the current password if required to reset their password 603 if ($this->requireReset == 1 && UserHelper::verifyPassword($this->password_clear, $this->password)) { 604 $this->setError(Text::_('JLIB_USER_ERROR_CANNOT_REUSE_PASSWORD')); 605 606 return false; 607 } 608 609 $array['password'] = UserHelper::hashPassword($array['password']); 610 611 // Reset the change password flag 612 $array['requireReset'] = 0; 613 } else { 614 $array['password'] = $this->password; 615 } 616 617 // Prevent updating internal fields 618 unset($array['registerDate']); 619 unset($array['lastvisitDate']); 620 unset($array['lastResetTime']); 621 unset($array['resetCount']); 622 } 623 624 if (\array_key_exists('params', $array)) { 625 $this->_params->loadArray($array['params']); 626 627 if (\is_array($array['params'])) { 628 $params = (string) $this->_params; 629 } else { 630 $params = $array['params']; 631 } 632 633 $this->params = $params; 634 } 635 636 // Bind the array 637 if (!$this->setProperties($array)) { 638 $this->setError(Text::_('JLIB_USER_ERROR_BIND_ARRAY')); 639 640 return false; 641 } 642 643 // Make sure its an integer 644 $this->id = (int) $this->id; 645 646 return true; 647 } 648 649 /** 650 * Method to save the User object to the database 651 * 652 * @param boolean $updateOnly Save the object only if not a new user 653 * Currently only used in the user reset password method. 654 * 655 * @return boolean True on success 656 * 657 * @since 1.7.0 658 * @throws \RuntimeException 659 */ 660 public function save($updateOnly = false) 661 { 662 // Create the user table object 663 $table = $this->getTable(); 664 $this->params = (string) $this->_params; 665 $table->bind($this->getProperties()); 666 667 // Allow an exception to be thrown. 668 try { 669 // Check and store the object. 670 if (!$table->check()) { 671 $this->setError($table->getError()); 672 673 return false; 674 } 675 676 // If user is made a Super Admin group and user is NOT a Super Admin 677 678 // @todo ACL - this needs to be acl checked 679 680 $my = Factory::getUser(); 681 682 // Are we creating a new user 683 $isNew = empty($this->id); 684 685 // If we aren't allowed to create new users return 686 if ($isNew && $updateOnly) { 687 return true; 688 } 689 690 // Get the old user 691 $oldUser = new User($this->id); 692 693 // Access Checks 694 695 // The only mandatory check is that only Super Admins can operate on other Super Admin accounts. 696 // To add additional business rules, use a user plugin and throw an Exception with onUserBeforeSave. 697 698 // Check if I am a Super Admin 699 $iAmSuperAdmin = $my->authorise('core.admin'); 700 701 $iAmRehashingSuperadmin = false; 702 703 if (($my->id == 0 && !$isNew) && $this->id == $oldUser->id && $oldUser->authorise('core.admin') && $oldUser->password != $this->password) { 704 $iAmRehashingSuperadmin = true; 705 } 706 707 // Check if we are using a CLI application 708 $isCli = false; 709 710 if (Factory::getApplication()->isCli()) { 711 $isCli = true; 712 } 713 714 // We are only worried about edits to this account if I am not a Super Admin. 715 if ($iAmSuperAdmin != true && $iAmRehashingSuperadmin != true && $isCli != true) { 716 // I am not a Super Admin, and this one is, so fail. 717 if (!$isNew && Access::check($this->id, 'core.admin')) { 718 throw new \RuntimeException('User not Super Administrator'); 719 } 720 721 if ($this->groups != null) { 722 // I am not a Super Admin and I'm trying to make one. 723 foreach ($this->groups as $groupId) { 724 if (Access::checkGroup($groupId, 'core.admin')) { 725 throw new \RuntimeException('User not Super Administrator'); 726 } 727 } 728 } 729 } 730 731 // Fire the onUserBeforeSave event. 732 PluginHelper::importPlugin('user'); 733 734 $result = Factory::getApplication()->triggerEvent('onUserBeforeSave', array($oldUser->getProperties(), $isNew, $this->getProperties())); 735 736 if (\in_array(false, $result, true)) { 737 // Plugin will have to raise its own error or throw an exception. 738 return false; 739 } 740 741 // Store the user data in the database 742 $result = $table->store(); 743 744 // Set the id for the User object in case we created a new user. 745 if (empty($this->id)) { 746 $this->id = $table->get('id'); 747 } 748 749 if ($my->id == $table->id) { 750 $registry = new Registry($table->params); 751 $my->setParameters($registry); 752 } 753 754 // Fire the onUserAfterSave event 755 Factory::getApplication()->triggerEvent('onUserAfterSave', array($this->getProperties(), $isNew, $result, $this->getError())); 756 } catch (\Exception $e) { 757 $this->setError($e->getMessage()); 758 759 return false; 760 } 761 762 return $result; 763 } 764 765 /** 766 * Method to delete the User object from the database 767 * 768 * @return boolean True on success 769 * 770 * @since 1.7.0 771 */ 772 public function delete() 773 { 774 PluginHelper::importPlugin('user'); 775 776 // Trigger the onUserBeforeDelete event 777 Factory::getApplication()->triggerEvent('onUserBeforeDelete', array($this->getProperties())); 778 779 // Create the user table object 780 $table = $this->getTable(); 781 782 if (!$result = $table->delete($this->id)) { 783 $this->setError($table->getError()); 784 } 785 786 // Trigger the onUserAfterDelete event 787 Factory::getApplication()->triggerEvent('onUserAfterDelete', array($this->getProperties(), $result, $this->getError())); 788 789 return $result; 790 } 791 792 /** 793 * Method to load a User object by user id number 794 * 795 * @param mixed $id The user id of the user to load 796 * 797 * @return boolean True on success 798 * 799 * @since 1.7.0 800 */ 801 public function load($id) 802 { 803 // Create the user table object 804 $table = $this->getTable(); 805 806 // Load the UserModel object based on the user id or throw a warning. 807 if (!$table->load($id)) { 808 // Reset to guest user 809 $this->guest = 1; 810 811 Log::add(Text::sprintf('JLIB_USER_ERROR_UNABLE_TO_LOAD_USER', $id), Log::WARNING, 'jerror'); 812 813 return false; 814 } 815 816 /* 817 * Set the user parameters using the default XML file. We might want to 818 * extend this in the future to allow for the ability to have custom 819 * user parameters, but for right now we'll leave it how it is. 820 */ 821 822 if ($table->params) { 823 $this->_params->loadString($table->params); 824 } 825 826 // Assuming all is well at this point let's bind the data 827 $this->setProperties($table->getProperties()); 828 829 // The user is no longer a guest 830 if ($this->id != 0) { 831 $this->guest = 0; 832 } else { 833 $this->guest = 1; 834 } 835 836 return true; 837 } 838 839 /** 840 * Method to allow serialize the object with minimal properties. 841 * 842 * @return array The names of the properties to include in serialization. 843 * 844 * @since 3.6.0 845 */ 846 public function __sleep() 847 { 848 return array('id'); 849 } 850 851 /** 852 * Method to recover the full object on unserialize. 853 * 854 * @return void 855 * 856 * @since 3.6.0 857 */ 858 public function __wakeup() 859 { 860 // Initialise some variables 861 $this->_params = new Registry(); 862 863 // Load the user if it exists 864 if (!empty($this->id) && $this->load($this->id)) { 865 // Push user into cached instances. 866 self::$instances[$this->id] = $this; 867 } else { 868 // Initialise 869 $this->id = 0; 870 $this->sendEmail = 0; 871 $this->aid = 0; 872 $this->guest = 1; 873 } 874 } 875 }
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 |