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