Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 137
0.00% covered (danger)
0.00%
0 / 29
CRAP
0.00% covered (danger)
0.00%
0 / 1
tao_models_classes_UserService
0.00% covered (danger)
0.00%
0 / 137
0.00% covered (danger)
0.00%
0 / 29
3660
0.00% covered (danger)
0.00%
0 / 1
 singleton
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 loginUser
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getCurrentUser
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 loginExists
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 loginAvailable
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getOneUser
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 getUserById
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 removeUser
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getAllowedRoles
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getDefaultRole
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 logout
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAllUsers
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getCountUsers
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 toTree
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 addUser
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 isASessionOpened
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isPasswordValid
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setPassword
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUserRoles
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 userHasRoles
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 attachRole
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 unnatachRole
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 attachProperties
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getRootClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 createInstance
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getPermittedRoles
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
42
 checkCurrentUserAccess
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
90
 triggerUpdatedEvent
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2002-2008 (original work) Public Research Centre Henri Tudor & University of Luxembourg
19 *                         (under the project TAO & TAO2);
20 *               2008-2010 (update and modification) Deutsche Institut für Internationale Pädagogische Forschung
21 *                         (under the project TAO-TRANSFER);
22 *               2009-2012 (update and modification) Public Research Centre Henri Tudor
23 *                         (under the project TAO-SUSTAIN & TAO-DEV);
24 *               2013-2014 (update and modification) Open Assessment Technologies SA (under the project TAO-PRODUCT);
25 */
26
27use oat\generis\model\GenerisRdf;
28use oat\generis\model\OntologyRdfs;
29use oat\oatbox\user\LoginService;
30use oat\oatbox\user\User;
31use oat\generis\model\user\UserRdf;
32use oat\tao\model\event\UserCreatedEvent;
33use oat\tao\model\event\UserRemovedEvent;
34use oat\tao\model\TaoOntology;
35use oat\tao\model\user\TaoRoles;
36use oat\oatbox\service\ServiceManager;
37use oat\tao\model\event\UserUpdatedEvent;
38use oat\oatbox\service\ConfigurableService;
39use oat\tao\model\ClassServiceTrait;
40use oat\tao\model\GenerisServiceTrait;
41use core_kernel_classes_Resource as Resource;
42use core_kernel_classes_Class as TypeClass;
43
44/**
45 * This class provide service on user management
46 */
47class tao_models_classes_UserService extends ConfigurableService implements core_kernel_users_UsersManagement
48{
49    use ClassServiceTrait;
50
51    use GenerisServiceTrait {
52        createInstance as protected traitCreateInstance;
53    }
54
55    public const SERVICE_ID = 'tao/UserService';
56
57    public const OPTION_ALLOW_API = 'allow_api';
58
59    /**
60     * the core user service
61     *
62     * @var    core_kernel_users_Service
63     */
64    protected $generisUserService;
65
66    /**
67     * @deprecated
68     */
69    public static function singleton()
70    {
71        return ServiceManager::getServiceManager()->get(self::SERVICE_ID);
72    }
73
74    /**
75     * constructor
76     *
77     * @param array $options
78     *
79     * @return mixed
80     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
81     */
82    public function __construct($options = [])
83    {
84        parent::__construct($options);
85        $this->generisUserService = core_kernel_users_Service::singleton();
86    }
87
88
89    /**
90     * authenticate a user
91     *
92     * @param string login
93     * @param string password
94     *
95     * @return     boolean
96     * @author     Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
97     * @deprecated
98     */
99    public function loginUser($login, $password)
100    {
101        $returnValue = false;
102        try {
103            $returnValue = LoginService::login($login, $password);
104        } catch (core_kernel_users_Exception $ue) {
105            common_Logger::e('A fatal error occured at user login time: ' . $ue->getMessage());
106        }
107
108        return $returnValue;
109    }
110
111
112    /**
113     * retrieve the logged in user
114     *
115     * @return Resource
116     * @throws common_exception_Error
117     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
118     */
119    public function getCurrentUser()
120    {
121        $returnValue = null;
122        if (!common_session_SessionManager::isAnonymous()) {
123            $userUri = \common_session_SessionManager::getSession()->getUser()->getIdentifier();
124            if (!empty($userUri)) {
125                $returnValue = new Resource($userUri);
126            } else {
127                common_Logger::d('no userUri');
128            }
129        }
130
131        return $returnValue;
132    }
133
134
135    /**
136     * Check if the login is already used
137     *
138     * @param string login
139     * @param TypeClass|null $class
140     *
141     * @return boolean
142     * @author Jerome Bogaerts, <jerome@taotesting.com>
143     */
144    public function loginExists($login, TypeClass $class = null)
145    {
146        return $this->generisUserService->loginExists($login, $class) ?? false;
147    }
148
149    /**
150     * Check if the login is available (because it's unique)
151     *
152     * @param string login
153     *
154     * @return boolean
155     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
156     */
157    public function loginAvailable($login)
158    {
159        $returnValue = false;
160
161        if (!empty($login)) {
162            $returnValue = !$this->loginExists($login);
163        }
164
165        return $returnValue;
166    }
167
168
169    /**
170     * Get a user that has a given login.
171     *
172     * @param string login the user login is the unique identifier to retrieve him.
173     * @param TypeClass A specific class to search the user.
174     *
175     * @return Resource
176     * @throws common_exception_InvalidArgumentType
177     * @author Jerome Bogaerts, <jerome@taotesting.com>
178     */
179    public function getOneUser($login, TypeClass $class = null)
180    {
181        $returnValue = null;
182        if (empty($login)) {
183            throw new common_exception_InvalidArgumentType('Missing login for ' . __FUNCTION__);
184        }
185
186        $user = $this->generisUserService->getOneUser($login, $class ?? $this->getRootClass());
187
188        if ($user !== null) {
189            $allowedRoles = $this->getAllowedRoles();
190            if ($this->generisUserService->userHasRoles($user, $allowedRoles)) {
191                $returnValue = $user;
192            } else {
193                common_Logger::i(sprintf('User found for login \'%s\' but does not have matching roles', $login));
194            }
195        } else {
196            common_Logger::i(sprintf('No user found for login \'%s\'', $login));
197        }
198
199        return $returnValue;
200    }
201
202    /**
203     * @param  $userId
204     *
205     * @return User
206     * @throws common_exception_Error
207     */
208    public function getUserById($userId)
209    {
210        if (is_string($userId)) {
211            $userId = new Resource($userId);
212        }
213
214        if ($userId instanceof Resource) {
215            $userId = new core_kernel_users_GenerisUser($userId);
216        }
217
218        if (!($userId instanceof core_kernel_users_GenerisUser)) {
219            common_Logger::i('Unable to get user from ' . $userId);
220            $userId = null;
221        }
222
223        return $userId;
224    }
225
226
227    /**
228     * Remove a user
229     *
230     * @param Resource $user
231     *
232     * @return boolean
233     * @throws common_exception_Error
234     * @author       Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
235     */
236    public function removeUser(Resource $user)
237    {
238        $returnValue = false;
239
240        if (!is_null($user)) {
241            $this->checkCurrentUserAccess($this->getUserRoles($user));
242            $returnValue = $this->generisUserService->removeUser($user);
243            $this->getEventManager()->trigger(new UserRemovedEvent($user->getUri()));
244        }
245
246        return $returnValue;
247    }
248
249    /**
250     * Returns a list of all concrete roles (instances of GenerisRdf::CLASS_ROLE)
251     * which are allowed to login
252     *
253     * @return array
254     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
255     */
256    public function getAllowedRoles()
257    {
258        return [TaoRoles::BACK_OFFICE => $this->getDefaultRole()];
259    }
260
261    public function getDefaultRole()
262    {
263        return new Resource(TaoRoles::BACK_OFFICE);
264    }
265
266    /**
267     * Short description of method logout
268     *
269     * @return boolean
270     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
271     */
272    public function logout()
273    {
274        return $this->generisUserService->logout();
275    }
276
277    /**
278     * Short description of method getAllUsers
279     *
280     * @param array $options
281     * @param array $filters
282     *
283     * @return core_kernel_classes_Resource[]
284     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
285     */
286    public function getAllUsers($options = [], $filters = [GenerisRdf::PROPERTY_USER_LOGIN => '*'])
287    {
288        $userClass = new TypeClass(TaoOntology::CLASS_URI_TAO_USER);
289        $options = array_merge(['recursive' => true, 'like' => true], $options);
290
291        return $userClass->searchInstances($filters, $options);
292    }
293
294
295    /**
296     * Returns count of instances, that match conditions in options and filters
297     *
298     * @param array $options
299     * @param array $filters
300     *
301     * @return integer
302     * @author Ivan Klimchuk <klimchuk@1pt.com>
303     */
304    public function getCountUsers($options = [], $filters = [])
305    {
306        $userClass = new TypeClass(TaoOntology::CLASS_URI_TAO_USER);
307
308        return $userClass->countInstances($filters, $options);
309    }
310
311    /**
312     * @author Jerome Bogaerts, <jerome.bogaerts@tudor.lu>
313     *
314     * @throws core_kernel_persistence_Exception
315     *
316     * @return array
317     */
318    public function toTree(TypeClass $clazz, array $options = [])
319    {
320        $returnValue = [];
321        $users = $this->getAllUsers(['order' => UserRdf::PROPERTY_LOGIN]);
322
323        foreach ($users as $user) {
324            $login = (string) $user->getOnePropertyValue($user->getProperty(UserRdf::PROPERTY_LOGIN));
325            $login = tao_helpers_Display::htmlEscape($login);
326            $login = tao_helpers_Display::textCutter($login, 16);
327
328            $returnValue[] = [
329                'data' => $login,
330                'attributes' => [
331                    'id' => tao_helpers_Uri::encode($user->getUri()),
332                    'class' => 'node-instance',
333                ],
334            ];
335        }
336
337        return $returnValue;
338    }
339
340    /**
341     * Add a new user.
342     *
343     * @param string login The login to give the user.
344     * @param string password the password in clear.
345     * @param Resource role A role to grant to the user.
346     * @param TypeClass A specific class to use to instantiate the new user.
347     * If not specified, the class returned by the getUserClass method is used.
348     *
349     * @return Resource the new user
350     * @throws core_kernel_users_Exception If an error occurs.
351     * @throws common_exception_Error
352     */
353    public function addUser(
354        $login,
355        $password,
356        Resource $role = null,
357        TypeClass $class = null
358    ) {
359        $this->checkCurrentUserAccess($role);
360
361        if ($class === null) {
362            $class = $this->getRootClass();
363        }
364
365        $user = $this->generisUserService->addUser($login, $password, $role, $class);
366        // set up default properties
367        if (!is_null($user)) {
368            $user->setPropertyValue(
369                new core_kernel_classes_Property(TaoOntology::PROPERTY_USER_FIRST_TIME),
370                GenerisRdf::GENERIS_TRUE
371            );
372        }
373
374        return $user;
375    }
376
377    /**
378     * Indicates if a user session is currently opened or not.
379     *
380     * @return boolean True if a session is opened, false otherwise.
381     */
382    public function isASessionOpened()
383    {
384        return common_user_auth_Service::singleton()->isASessionOpened();
385    }
386
387
388    /**
389     * Indicates if a given user has a given password.
390     *
391     * @param string password The password to check.
392     * @param Resource user The user you want to check the password.
393     *
394     * @return boolean
395     * @throws core_kernel_users_Exception
396     */
397    public function isPasswordValid($password, Resource $user)
398    {
399        return $this->generisUserService->isPasswordValid($password, $user);
400    }
401
402    /**
403     * Change the password of a given user.
404     *
405     * @param Resource $user
406     * @param Resource user The user you want to change the password.
407     *
408     * @throws core_kernel_users_Exception
409     */
410    public function setPassword(Resource $user, $password)
411    {
412        $this->generisUserService->setPassword($user, $password);
413    }
414
415    /**
416     * Get the roles of a given user.
417     *
418     * @param Resource $user The user you want to retrieve the roles.
419     *
420     * @return array An array of Resource.
421     */
422    public function getUserRoles(Resource $user)
423    {
424        return $this->generisUserService->getUserRoles($user);
425    }
426
427    /**
428     * Indicates if a user is granted with a set of Roles.
429     *
430     * @param Resource $user
431     * @param Resource user The User instance you want to check Roles.
432     *
433     * @return boolean
434     * @author Jerome Bogaerts, <jerome@taotesting.com>
435     */
436    public function userHasRoles(Resource $user, $roles)
437    {
438        return $this->generisUserService->userHasRoles($user, $roles);
439    }
440
441    /**
442     * Attach a Generis Role to a given TAO User. A UserException will be
443     * if an error occurs. If the User already has the role, nothing happens.
444     *
445     * @param Resource $user
446     * @param Resource $role
447     *
448     * @throws common_exception_Error
449     * @throws core_kernel_users_Exception If an error occurs.*@throws common_exception_Error
450     * @author Jerome Bogaerts, <jerome@taotesting.com>
451     */
452    public function attachRole(Resource $user, Resource $role)
453    {
454        // check that current user has rights to set this role
455        $this->checkCurrentUserAccess($role);
456        $this->generisUserService->attachRole($user, $role);
457    }
458
459    /**
460     * Un-attach a Role from a given TAO User.
461     *
462     * @param Resource $user
463     * @param Resource $role
464     *
465     * @author Jerome Bogaerts, <jerome@taotesting.com>
466     */
467    public function unnatachRole(Resource $user, Resource $role)
468    {
469        try {
470            $this->checkCurrentUserAccess($role);
471            $this->generisUserService->unnatachRole($user, $role);
472        } catch (common_exception_Error $e) {
473        }
474    }
475
476    /**
477     * @param Resource $user
478     * @param array    $properties
479     */
480    public function attachProperties(Resource $user, array $properties)
481    {
482        if (array_key_exists(OntologyRdfs::RDFS_LABEL, $properties)) {
483            $label = $properties[OntologyRdfs::RDFS_LABEL];
484            unset($properties[OntologyRdfs::RDFS_LABEL]);
485            $user->setLabel($label);
486        }
487
488        $user->setPropertiesValues($properties);
489    }
490
491    /**
492     * Get the class to use to instantiate users.
493     *
494     * @return TypeClass The user class.
495     */
496    public function getRootClass()
497    {
498        return new TypeClass(TaoOntology::CLASS_URI_TAO_USER);
499    }
500
501    /**
502     * @param TypeClass $clazz
503     * @param string    $label
504     *
505     * @return Resource
506     * @throws common_exception_Error
507     */
508    public function createInstance(TypeClass $clazz, $label = '')
509    {
510        $user = $this->traitCreateInstance($clazz, $label);
511        $this->getEventManager()->trigger(new UserCreatedEvent($user));
512        return $user;
513    }
514
515    /**
516     * Filter roles to leave only permitted roles
517     *
518     * @param Resource $user
519     * @param array    $roles
520     * @param bool     $encoded
521     *
522     * @return array
523     */
524    public function getPermittedRoles(Resource $user, array $roles, $encoded = true)
525    {
526        $exclude = [];
527        if (!$this->userHasRoles($user, TaoRoles::SYSTEM_ADMINISTRATOR)) {
528            $exclude[] = $encoded ? tao_helpers_Uri::encode(
529                TaoRoles::SYSTEM_ADMINISTRATOR
530            ) : TaoRoles::SYSTEM_ADMINISTRATOR;
531            if (!$this->userHasRoles($user, TaoRoles::GLOBAL_MANAGER)) {
532                $exclude[] = $encoded ? tao_helpers_Uri::encode(TaoRoles::GLOBAL_MANAGER) : TaoRoles::GLOBAL_MANAGER;
533            }
534        }
535
536        if (count($exclude)) {
537            $roles = array_filter(
538                $roles,
539                function ($k) use ($exclude) {
540                    return !in_array($k, $exclude);
541                },
542                ARRAY_FILTER_USE_KEY
543            );
544        }
545
546        return $roles;
547    }
548
549
550    /**
551     * Thrown an exception if user doesn't have permissions
552     *
553     * @param  $roles
554     *
555     * @throws common_exception_Error
556     */
557    public function checkCurrentUserAccess($roles)
558    {
559        if ($this->getCurrentUser() === null) {
560            return;
561        }
562
563        if ($roles instanceof Resource) {
564            $roles = [$roles->getUri()];
565        }
566
567        if (is_array($roles)) {
568            $roles = array_map(
569                function ($role) {
570                    return $role instanceof Resource ? $role->getUri() : $role;
571                },
572                $roles
573            );
574        }
575
576        if (
577            in_array(TaoRoles::SYSTEM_ADMINISTRATOR, $roles, true)
578            && !$this->userHasRoles($this->getCurrentUser(), TaoRoles::SYSTEM_ADMINISTRATOR)
579        ) {
580            throw new common_exception_Error('Permission denied');
581        }
582
583        if (
584            in_array(TaoRoles::GLOBAL_MANAGER, $roles, true)
585            && !$this->userHasRoles($this->getCurrentUser(), [TaoRoles::SYSTEM_ADMINISTRATOR, TaoRoles::GLOBAL_MANAGER])
586        ) {
587            throw new common_exception_Error('Permission denied');
588        }
589    }
590
591    /**
592     * @param Resource    $user
593     * @param array       $values
594     * @param string|null $hashForKey
595     *
596     * @return boolean
597     * @throws tao_models_classes_dataBinding_GenerisFormDataBindingException
598     */
599    public function triggerUpdatedEvent(Resource $user, array $values, $hashForKey)
600    {
601        $triggered = false;
602        $binder = new tao_models_classes_dataBinding_GenerisFormDataBinder($user);
603
604        if ($binder->bind($values)) {
605            if ($hashForKey !== null) {
606                $values['hashForKey'] = $hashForKey;
607            }
608
609            $this->getEventManager()->trigger(new UserUpdatedEvent($user, $values));
610            $triggered = true;
611        }
612
613        return $triggered;
614    }
615}