wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.UserManager.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2014 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id: class.UserManager.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/lib/core/class.WCMFException.php");
20 require_once(BASE."wcmf/lib/util/class.Message.php");
21 require_once(BASE."wcmf/lib/persistence/class.PersistenceFacade.php");
22 require_once(BASE."wcmf/lib/util/class.InifileParser.php");
23 
24 /**
25  * Some constants describing user properties
26  */
27 define("USER_PROPERTY_LOGIN", "login");
28 define("USER_PROPERTY_NAME", "name");
29 define("USER_PROPERTY_FIRSTNAME", "firstname");
30 define("USER_PROPERTY_CONFIG", "config");
31 define("ROLE_PROPERTY_NAME", "name");
32 
33 /**
34  * @class UserManager
35  * @ingroup Security
36  * @brief UserManager is used to edit users and roles.
37  * UserManager supports the following operations:
38  * - create/remove a user
39  * - create/remove a role
40  * - add/remove a user to/from a role
41  * - change a users password
42  *
43  * This class defines abstract methods that subclasses must implement to support
44  * different user repositories. The UserManager implementation class is defined by
45  * the configuration key 'UserManager' in the [implementation] section.
46  *
47  * @todo - Use RightsManager to restrict access to methods (except changePassword)
48  * - Transaction support
49  *
50  * @author ingo herwig <ingo@wemove.com>
51  */
53 {
54  var $_initParams = null;
55  var $_userRepository = null;
56  var $_roleConfig = null;
57 
58  /**
59  * Creates a UserManager Object.
60  * @param params Initialization data given in an associative array as needed to load the user repository
61  */
62  function UserManager($params)
63  {
64  $this->_initParams = $params;
65  $this->_userRepository = $this->initialize($this->_initParams);
66 
67  // load role config if existing
68  $parser = &InifileParser::getInstance();
69  if (($roleConfig = $parser->getSection('roleconfig')) !== false) {
70  $this->_roleConfig = $roleConfig;
71  }
72  }
73 
74  /**
75  * Encrypt a password using the md5 algorithm.
76  * @param password The password to encrypt
77  * @return The encrypted password.
78  */
79  function encryptPassword($password)
80  {
81  return md5($password);
82  }
83 
84  /**
85  * Internal error method. Rollback transaction and call WCMFException::throwEx.
86  */
87  function onError($message, $file='', $line='')
88  {
89  $this->rollbackTransaction();
90  WCMFException::throwEx($message, $file, $line);
91  }
92 
93  /**
94  * Start a transaction. If implemented, the UserManager will collect a number of actions
95  * and execute them on commitTransaction().
96  * If not implemented the UserManager will execute these actions on every call of the appropriate function.
97  */
98  function startTransaction() {}
99 
100  /**
101  * Commit a transaction. If implemented, the UserManager will execute a number of actions
102  * that it collected since the call to startTransaction().
103  * If not implemented the UserManager will execute these actions on every call of the appropriate function.
104  */
105  function commitTransaction() {}
106 
107  /**
108  * Rollback a transaction. If implemented, the UserManager will rollback a number of actions
109  * that it collected since the call to startTransaction().
110  * If not implemented the UserManager will execute these actions on every call of the appropriate function.
111  */
112  function rollbackTransaction() {}
113 
114  /**
115  * Create a user login with a given password.
116  * @param name The name of the user
117  * @param firstname The first name of the user
118  * @param login The login of the user
119  * @param password The password of the user
120  * @param passwordRepeated The password of the user again
121  * @return A reference to the created user.
122  */
123  function &createUser($name, $firstname, $login, $password, $passwordRepeated)
124  {
125  if ($password != $passwordRepeated) {
126  $this->onError(Message::get("The given passwords don't match"), __FILE__, __LINE__);
127  }
128  if ($login == '') {
129  $this->onError(Message::get("The user requires a login name"), __FILE__, __LINE__);
130  }
131  if ($this->getUser($login) != null) {
132  $this->onError(Message::get("The login '%1%' already exists", array($login)), __FILE__, __LINE__);
133  }
134  // encrypt password
135  $password = $this->encryptPassword($password);
136 
137  $user = &$this->createUserImpl($name, $firstname, $login, $password);
138 
139  // update user repository
140  $this->_userRepository['users'][sizeof($this->_userRepository['users'])] = &$user;
141 
142  return $user;
143  }
144 
145  /**
146  * Remove a user login.
147  * @param login The login of the user
148  */
149  function removeUser($login)
150  {
151  if (($user = $this->getUser($login)) == null) {
152  $this->onError(Message::get("The login '%1%' does not exist", array($login)), __FILE__, __LINE__);
153  }
154  $this->removeUserImpl($user);
155 
156  // update user repository
157  for($i=0; $i<sizeof($this->_userRepository['users']); $i++)
158  {
159  if ($this->_userRepository['users'][$i]->getLogin() == $login) {
160  array_splice($this->_userRepository['users'][$i], $i, 1);
161  }
162  }
163  }
164 
165  /**
166  * Set a user property.
167  * @param login The login of the user
168  * @param property One of the USER_PROPERTY constants
169  * @param value The value to set this property to
170  */
171  function setUserProperty($login, $property, $value)
172  {
174  $this->onError(Message::get("Unknown user property: '%1%'", array($property)), __FILE__, __LINE__);
175  }
176  if (($property == USER_PROPERTY_LOGIN) && ($value == '')) {
177  $this->onError(Message::get("The user requires a login name"), __FILE__, __LINE__);
178  }
179  if (($property == USER_PROPERTY_LOGIN) && ($this->getUser($value) != null)) {
180  $this->onError(Message::get("The login '%1%' already exists", array($value)), __FILE__, __LINE__);
181  }
182  if (($user = $this->getUser($login)) == null) {
183  $this->onError(Message::get("The login '%1%' does not exist", array($login)), __FILE__, __LINE__);
184  }
185  // get the repository user first, because the login may change
186  $user = &$this->getUser($login);
187 
188  // validate the value
189  $validationMsg = $user->validateValue($property, $value, DATATYPE_ATTRIBUTE);
190  if (strlen($validationMsg) > 0) {
191  $this->onError($validationMsg, __FILE__, __LINE__);
192  }
193  else {
194  $this->setUserPropertyImpl($user, $property, $value);
195  }
196 
197  // update user repository
198  $user->setValue($property, $value, DATATYPE_ATTRIBUTE);
199  }
200 
201  /**
202  * Reset a users password.
203  * @param login The login of the user
204  * @param newPassword The new password for the user
205  * @param newPasswordRepeated The new password of the user again
206  */
207  function resetPassword($login, $newPassword, $newPasswordRepeated)
208  {
209  if (($user = $this->getUser($login)) == null) {
210  $this->onError(Message::get("The login '%1%' does not exist", array($login)), __FILE__, __LINE__);
211  }
212 
213  if ($newPassword != $newPasswordRepeated) {
214  $this->onError(Message::get("The given passwords don't match"), __FILE__, __LINE__);
215  }
216  // encrypt password
217  $newPassword = $this->encryptPassword($newPassword);
218 
219  $this->setUserPropertyImpl($user, 'password', $newPassword);
220 
221  // update user repository
222  $user = &$this->getUser($login);
223  $user->setPassword($newPassword);
224  }
225 
226  /**
227  * Change a users password.
228  * @param login The login of the user
229  * @param oldPassword The old password of the user
230  * @param newPassword The new password for the user
231  * @param newPasswordRepeated The new password of the user again
232  */
233  function changePassword($login, $oldPassword, $newPassword, $newPasswordRepeated)
234  {
235  if (($user = $this->getUser($login)) == null) {
236  $this->onError(Message::get("The login '%1%' does not exist", array($login)), __FILE__, __LINE__);
237  }
238  // encrypt password
239  $oldPassword = $this->encryptPassword($oldPassword);
240 
241  if ($user->getPassword() != $oldPassword) {
242  $this->onError(Message::get("The old password is incorrect"), __FILE__, __LINE__);
243  }
244  if ($newPassword != $newPasswordRepeated) {
245  $this->onError(Message::get("The given passwords don't match"), __FILE__, __LINE__);
246  }
247  // encrypt password
248  $newPassword = $this->encryptPassword($newPassword);
249 
250  $this->setUserPropertyImpl($user, 'password', $newPassword);
251 
252  // update user repository
253  $user = &$this->getUser($login);
254  $user->setPassword($newPassword);
255  }
256 
257  /**
258  * Create a role.
259  * @param name The name of the role
260  * @return A reference to the created role.
261  */
262  function &createRole($name)
263  {
264  if ($name == '') {
265  $this->onError(Message::get("The role requires a name"), __FILE__, __LINE__);
266  }
267  if ($this->getRole($name) != null) {
268  $this->onError(Message::get("The role '%1%' already exists", array($name)), __FILE__, __LINE__);
269  }
270  $role = &$this->createRoleImpl($name);
271 
272  // update user repository
273  $this->_userRepository['roles'][sizeof($this->_userRepository['roles'])] = &$role;
274 
275  return $role;
276  }
277 
278  /**
279  * Remove a role.
280  * @param name The name of the role
281  */
282  function removeRole($name)
283  {
284  if (($role = $this->getRole($name)) == null) {
285  $this->onError(Message::get("The role '%1%' does not exist", array($name)), __FILE__, __LINE__);
286  }
287  $this->removeRoleImpl($role);
288 
289  // update user repository
290  for($i=0; $i<sizeof($this->_userRepository['roles']); $i++)
291  {
292  if ($this->_userRepository['roles'][$i]->getName() == $name) {
293  array_splice($this->_userRepository['roles'][$i], $i, 1);
294  }
295  }
296  }
297 
298  /**
299  * Set a role property.
300  * @param name The name of the role
301  * @param property One of the ROLE_PROPERTY constants
302  * @param value The value to set this property to
303  */
304  function setRoleProperty($name, $property, $value)
305  {
306  if (!in_array($property, array(ROLE_PROPERTY_NAME))) {
307  $this->onError(Message::get("Unknown role property: '%1%'", array($property)), __FILE__, __LINE__);
308  }
309  if (($property == ROLE_PROPERTY_NAME) && ($value == '')) {
310  $this->onError(Message::get("The role requires a name"), __FILE__, __LINE__);
311  }
312  if (($property == ROLE_PROPERTY_NAME) && ($this->getRole($value) != null)) {
313  $this->onError(Message::get("The role '%1%' already exists", array($value)), __FILE__, __LINE__);
314  }
315  if (($role = $this->getRole($name)) == null) {
316  $this->onError(Message::get("The role '%1%' does not exist", array($name)), __FILE__, __LINE__);
317  }
318  // get the repository role first, because the name may change
319  $role = &$this->getRole($name);
320 
321  // validate the value
322  $validationMsg = $role->validateValue($property, $value, DATATYPE_ATTRIBUTE);
323  if (strlen($validationMsg) > 0) {
324  $this->onError($validationMsg, __FILE__, __LINE__);
325  }
326  else {
327  $this->setRolePropertyImpl($role, $property, $value);
328  }
329 
330  // update user repository
331  $role->setValue($property, $value, DATATYPE_ATTRIBUTE);
332  }
333 
334  /**
335  * Add a user to a role.
336  * @param rolename The name of the role
337  * @param login The login of the user
338  */
339  function addUserToRole($rolename, $login)
340  {
341  if (($role = $this->getRole($rolename)) == null) {
342  $this->onError(Message::get("The role '%1%' does not exist", array($rolename)), __FILE__, __LINE__);
343  }
344  if (($user = $this->getUser($login)) == null) {
345  $this->onError(Message::get("The login '%1%' does not exist", array($login)), __FILE__, __LINE__);
346  }
347  if ($user != null && $user->hasRole($rolename)) {
348  $this->onError(Message::get("The user '%1%' already has the role '%2%'", array($login, $rolename)), __FILE__, __LINE__);
349  }
350  $this->addUserToRoleImpl($role, $user);
351 
352  // set role config
353  if ($this->_roleConfig && isset($this->_roleConfig[$rolename])) {
354  $this->setUserProperty($login, USER_PROPERTY_CONFIG, $this->_roleConfig[$rolename]);
355  }
356  }
357 
358  /**
359  * Remove a user from a role.
360  * @param rolename The name of the role
361  * @param login The login of the user
362  */
363  function removeUserFromRole($rolename, $login)
364  {
365  if (($role = $this->getRole($rolename)) == null) {
366  $this->onError(Message::get("The role '%1%' does not exist", array($rolename)), __FILE__, __LINE__);
367  }
368  if (($user = $this->getUser($login)) == null) {
369  $this->onError(Message::get("The login '%1%' does not exist", array($login)), __FILE__, __LINE__);
370  }
371  if ($user != null && !$user->hasRole($rolename)) {
372  $this->onError(Message::get("The user '%1%' does not have the role '%2%'", array($login, $rolename)), __FILE__, __LINE__);
373  }
374  $this->removeUserFromRoleImpl($role, $user);
375 
376  // remove role config
377  if ($this->_roleConfig && isset($this->_roleConfig[$rolename])) {
378  $this->setUserProperty($login, USER_PROPERTY_CONFIG, '');
379  }
380  }
381 
382  /**
383  * Get list of all users.
384  * @return An array containing all login names
385  */
386  function listUsers()
387  {
388  $result = array();
389  for($i=0; $i<sizeof($this->_userRepository['users']); $i++) {
390  array_push($result, $this->_userRepository['users'][$i]->getLogin());
391  }
392  return $result;
393  }
394 
395  /**
396  * Get list of all roles.
397  * @return An array containing all role names
398  */
399  function listRoles()
400  {
401  $result = array();
402  for($i=0; $i<sizeof($this->_userRepository['roles']); $i++) {
403  array_push($result, $this->_userRepository['roles'][$i]->getName());
404  }
405  return $result;
406  }
407 
408  /**
409  * Get list of all roles a user has.
410  * @return An array containing all role names of the user
411  */
412  function listUserRoles($login)
413  {
414  if (($user = &$this->getUser($login)) == null) {
415  $this->onError(Message::get("The login '%1%' does not exist", array($login)), __FILE__, __LINE__);
416  }
417  $roles = $user->getRoles();
418  $result = array();
419  for($i=0; $i<sizeof($roles); $i++) {
420  array_push($result, $roles[$i]->getName());
421  }
422  return $result;
423  }
424 
425  /**
426  * Get list of all users that have a role.
427  * @return An array containing all login names of the role members
428  */
429  function listRoleMembers($rolename)
430  {
431  if (($role = &$this->getRole($rolename)) == null) {
432  $this->onError(Message::get("The role '%1%' does not exist", array($rolename)), __FILE__, __LINE__);
433  }
434  $result = array();
435  for($i=0; $i<sizeof($this->_userRepository['users']); $i++)
436  {
437  $curUser = &$this->_userRepository['users'][$i];
438  $roles = $this->listUserRoles($curUser->getLogin());
439  if (in_array($rolename, $roles)) {
440  array_push($result, $curUser->getLogin());
441  }
442  }
443  return $result;
444  }
445 
446  /**
447  * Get a user from the repository.
448  * @param login The login of the user
449  * @return A reference to the matching User object or null if the user does not exist
450  */
451  function &getUser($login)
452  {
453  for($i=0; $i<sizeof($this->_userRepository['users']); $i++)
454  {
455  $curUser = &$this->_userRepository['users'][$i];
456  if ($curUser->getLogin() == $login) {
457  return $curUser;
458  }
459  }
460  return null;
461  }
462 
463  /**
464  * Get a role from the repository.
465  * @param name The name of the role
466  * @return A reference to the matching Role object or null if the role does not exist
467  */
468  function &getRole($name)
469  {
470  for($i=0; $i<sizeof($this->_userRepository['roles']); $i++)
471  {
472  $curRole = &$this->_userRepository['roles'][$i];
473  if ($curRole->getName() == $name) {
474  return $curRole;
475  }
476  }
477  return null;
478  }
479 
480  /**
481  * Get a principal (type: user/role) from the repository.
482  * @param oid The oid of the principal
483  * @return A reference to the matching User/Role object or null if the principal does not exist
484  */
485  function &getPrincipal($oid)
486  {
487  $principal = null;
488  $oidParts = PersistenceFacade::decomposeOID($oid);
489  if ($oidParts['type'] == UserManager::getUserClassName()) {
490  $principalArray = &$this->_userRepository['users'];
491  }
492  elseif ($oidParts['type'] == UserManager::getRoleClassName()) {
493  $principalArray = &$this->_userRepository['roles'];
494  }
495  else {
496  $this->onError(Message::get("Unknown object type: '%1%'", array($oidParts['type'])), __FILE__, __LINE__);
497  }
498  for($i=0; $i<sizeof($principalArray); $i++)
499  {
500  $curPrincipal = &$principalArray[$i];
501  if ($curPrincipal->getDBID() == $oidParts['id'][0]) {
502  $principal = &$curPrincipal;
503  }
504  }
505  return $principal;
506  }
507 
508  /**
509  * Remove a principal (type: user/role) from the repository.
510  * @param oid The oid of the principal
511  */
512  function removePrincipal($oid)
513  {
514  $oidParts = PersistenceFacade::decomposeOID($oid);
515 
516  $principal = $this->getPrincipal($oid);
517  if ($principal != null)
518  {
519  if ($oidParts['type'] == UserManager::getUserClassName()) {
520  $this->removeUser($principal->getLogin());
521  }
522  elseif ($oidParts['type'] == UserManager::getRoleClassName()) {
523  $this->removeRole($principal->getName());
524  }
525  else {
526  $this->onError(Message::get("Unknown object type: '%1%'", array($oidParts['type'])), __FILE__, __LINE__);
527  }
528  }
529  else {
530  $this->onError(Message::get("The principal does not exist: '%1%'", array($oid)), __FILE__, __LINE__);
531  }
532  }
533 
534  /**
535  * Get the user implemenataion class name as configured in config section
536  * 'implementation' key 'User'. Maybe used in a static way.
537  * @return The class name
538  */
539  function getUserClassName()
540  {
541  $parser = &InifileParser::getInstance();
542  if (($className = $parser->getValue('User', 'implementation')) === false) {
543  $this->onError($parser->getErrorMsg());
544  }
545  return $className;
546  }
547 
548  /**
549  * Get the role implemenataion class name as configured in config section
550  * 'implementation' key 'Role'. Maybe used in a static way.
551  * @return The class name
552  */
553  function getRoleClassName()
554  {
555  $parser = &InifileParser::getInstance();
556  if (($className = $parser->getValue('Role', 'implementation')) === false) {
557  $this->onError($parser->getErrorMsg());
558  }
559  return $className;
560  }
561 
562  /**
563  * Methods to be implemented by subclasses.
564  */
565 
566  /**
567  * Get the user and roles. This method is called before any operation on the repository.
568  * Subclasses will override this method to get the data from the application repository.
569  * @param params Initialization data given in an associative array as needed to load the user repository
570  * @return An assoziative array with the keys 'users' and 'roles' holding user and role objects respectively
571  */
572  function initialize($params)
573  {
574  WCMFException::throwEx("initialize() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
575  }
576 
577  /**
578  * Create a user login with a given password.
579  * @param name The name of the user
580  * @param firstname The first name of the user
581  * @param login The login of the user
582  * @param password The encrypted password of the user
583  * @return A reference to the created user
584  * @note Precondition: The login does not exist
585  */
586  function &createUserImpl($name, $firstname, $login, $password)
587  {
588  WCMFException::throwEx("createUserImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
589  }
590 
591  /**
592  * Remove a user.
593  * @param user A reference to the user
594  * @note This method is responsible for removing the user from all roles it has
595  * @note Precondition: The login does exist
596  */
597  function removeUserImpl(&$user)
598  {
599  WCMFException::throwEx("removeUserImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
600  }
601 
602  /**
603  * Set a user property.
604  * @param user A reference to the user
605  * @param property One of the USER_PROPERTY constants or 'password'
606  * @param value The value to set this property to
607  */
608  function setUserPropertyImpl(&$user, $property, $value)
609  {
610  WCMFException::throwEx("setUserPropertyImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
611  }
612 
613  /**
614  * Create a role.
615  * @param name The name of the role
616  * @return A reference to the created role
617  * @note Precondition: The role does not exist
618  */
619  function &createRoleImpl($name)
620  {
621  WCMFException::throwEx("createRoleImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
622  }
623 
624  /**
625  * Remove a role.
626  * @param role A reference to the role
627  * @note This method is responsible for removing the role from all users it is attached to
628  * @note Precondition: The role does exist
629  */
630  function removeRoleImpl(&$role)
631  {
632  WCMFException::throwEx("removeRoleImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
633  }
634 
635  /**
636  * Set a role property.
637  * @param role A reference to the role
638  * @param property One of the ROLE_PROPERTY constants
639  * @param value The value to set this property to
640  */
641  function setRolePropertyImpl(&$role, $property, $value)
642  {
643  WCMFException::throwEx("setRolePropertyImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
644  }
645 
646  /**
647  * Add a user to a role.
648  * @param role A reference to the role
649  * @param user A reference to the user
650  * @note Precondition: user and role do exist and the user does not have the role
651  */
652  function addUserToRoleImpl(&$role, &$user)
653  {
654  WCMFException::throwEx("addUserToRoleImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
655  }
656 
657  /**
658  * Remove a user from a role.
659  * @param role A reference to the role
660  * @param user A reference to the user
661  * @note Precondition: user and role do exist and the user does have the role
662  */
663  function removeUserFromRoleImpl(&$role, &$user)
664  {
665  WCMFException::throwEx("removeUserFromRoleImpl() must be implemented by derived class: ".get_class($this), __FILE__, __LINE__);
666  }
667 }
668 ?>
resetPassword($login, $newPassword, $newPasswordRepeated)
removeUserImpl(&$user)
const USER_PROPERTY_LOGIN
get($message, $parameters=null, $domain='', $lang='')
addUserToRoleImpl(&$role, &$user)
const ROLE_PROPERTY_NAME
const DATATYPE_ATTRIBUTE
changePassword($login, $oldPassword, $newPassword, $newPasswordRepeated)
removeUserFromRoleImpl(&$role, &$user)
onError($message, $file='', $line='')
const USER_PROPERTY_NAME
setRoleProperty($name, $property, $value)
throwEx($message, $file='', $line='')
& createUser($name, $firstname, $login, $password, $passwordRepeated)
setRolePropertyImpl(&$role, $property, $value)
& createUserImpl($name, $firstname, $login, $password)
UserManager is used to edit users and roles. UserManager supports the following operations: ...
decomposeOID($oid, $validate=true)
encryptPassword($password)
setUserProperty($login, $property, $value)
addUserToRole($rolename, $login)
& createRoleImpl($name)
const USER_PROPERTY_FIRSTNAME
removeRoleImpl(&$role)
UserManager($params)
listRoleMembers($rolename)
setUserPropertyImpl(&$user, $property, $value)
const USER_PROPERTY_CONFIG
removeUserFromRole($rolename, $login)