Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
DataAccessControl
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 7
380
0.00% covered (danger)
0.00%
0 / 1
 setParameterFilter
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 hasAccess
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 hasPrivileges
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
72
 hasWritePrivilege
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getPermissionProvider
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getParameterFilter
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getAdvancedLogger
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
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) 2014-2021 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 */
20
21declare(strict_types=1);
22
23namespace oat\tao\model\accessControl\data;
24
25use oat\oatbox\user\User;
26use Psr\Log\LoggerInterface;
27use oat\tao\model\lock\LockManager;
28use oat\tao\helpers\ControllerHelper;
29use oat\oatbox\service\ServiceManager;
30use oat\oatbox\log\logger\AdvancedLogger;
31use oat\tao\model\accessControl\AccessControl;
32use oat\generis\model\data\permission\PermissionManager;
33use oat\tao\model\controllerMap\ActionNotFoundException;
34use oat\generis\model\data\permission\PermissionInterface;
35use oat\oatbox\log\logger\extender\ContextExtenderInterface;
36use oat\tao\model\accessControl\filter\ParameterFilterProxy;
37use oat\tao\model\accessControl\filter\ParameterFilterInterface;
38
39class DataAccessControl implements AccessControl
40{
41    /** @var ParameterFilterInterface */
42    private $filter;
43
44    public function setParameterFilter(ParameterFilterInterface $filter): self
45    {
46        $this->filter = $filter;
47
48        return $this;
49    }
50
51    /**
52     * {@inheritdoc}
53     */
54    public function hasAccess(User $user, $controller, $action, $requestParameters)
55    {
56        $required = [];
57
58        try {
59            $requiredRights = ControllerHelper::getRequiredRights($controller, $action);
60            $uris = $this->getParameterFilter()->filter($requestParameters, array_keys($requiredRights));
61
62            foreach ($uris as $name => $urisValue) {
63                $required[] = array_fill_keys($urisValue, $requiredRights[$name]);
64            }
65        } catch (ActionNotFoundException $exception) {
66            $this->getAdvancedLogger()->error(
67                $exception->getMessage(),
68                [ContextExtenderInterface::CONTEXT_EXCEPTION => $exception]
69            );
70
71            return false;
72        }
73
74        return empty($required) || $this->hasPrivileges($user, array_merge(...$required));
75    }
76
77    /**
78     * Whenever or not the user has the required rights
79     *
80     * required takes the form of:
81     *   resourceId => $right
82     *
83     * @return bool
84     */
85    public function hasPrivileges(User $user, array $required)
86    {
87        foreach ($required as $resourceId => $right) {
88            if ($right === 'WRITE' && !$this->hasWritePrivilege($user, $resourceId)) {
89                $this->getAdvancedLogger()->info(
90                    'User does not have required permissions for the resource.',
91                    [
92                        'requiredPermission' => $right,
93                        'resourceId' => $resourceId,
94                    ]
95                );
96
97                return false;
98            }
99
100            if (!in_array($right, $this->getPermissionProvider()->getSupportedRights())) {
101                $required[$resourceId] = PermissionInterface::RIGHT_UNSUPPORTED;
102            }
103        }
104
105        $permissions = $this->getPermissionProvider()->getPermissions($user, array_keys($required));
106
107        foreach ($required as $id => $right) {
108            if (!isset($permissions[$id]) || !in_array($right, $permissions[$id], true)) {
109                $this->getAdvancedLogger()->info(
110                    'User does not have required permission for the resource.',
111                    [
112                        'requiredPermission' => $right,
113                        'resourceId' => $id,
114                        'resourcePermissions' => $permissions[$id] ?? [],
115                    ]
116                );
117
118                return false;
119            }
120        }
121
122        return true;
123    }
124
125    private function hasWritePrivilege(User $user, $resourceId)
126    {
127        $resource = new \core_kernel_classes_Resource($resourceId);
128        $lock = LockManager::getImplementation()->getLockData($resource);
129
130        return is_null($lock) || $lock->getOwnerId() == $user->getIdentifier();
131    }
132
133    public function getPermissionProvider()
134    {
135        return PermissionManager::getPermissionModel();
136    }
137
138    private function getParameterFilter(): ParameterFilterInterface
139    {
140        if (!$this->filter) {
141            $this->filter = new ParameterFilterProxy();
142        }
143
144        return $this->filter;
145    }
146
147    private function getAdvancedLogger(): LoggerInterface
148    {
149        return ServiceManager::getServiceManager()->getContainer()->get(AdvancedLogger::ACL_SERVICE_ID);
150    }
151}