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