Completed
Push — impl-EZP-26000-permission-look... ( 500ba3 )
by
unknown
26:49
created

PermissionResolver   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 209
Duplicated Lines 12.92 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 27
loc 209
rs 10
wmc 28
lcom 1
cbo 11

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
B resolvePermissions() 0 25 3
D resolvePermission() 18 84 13
C getPermissions() 9 34 11

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\Core\Repository\PermissionResolver;
8
9
use eZ\Publish\API\Repository\Values\ValueObject;
10
use eZ\Publish\API\Repository\Values\User\UserReference;
11
use eZ\Publish\Core\Repository\Helper\LimitationService;
12
use eZ\Publish\SPI\Limitation\Type as LimitationType;
13
use eZ\Publish\API\Repository\Values\User\Limitation;
14
use eZ\Publish\SPI\Persistence\User\Handler as UserHandler;
15
use eZ\Publish\Core\Repository\Helper\RoleDomainMapper;
16
17
/**
18
 * todo
19
 */
20
class PermissionResolver
21
{
22
    /**
23
     * @var \eZ\Publish\Core\Repository\Helper\RoleDomainMapper
24
     */
25
    private $roleDomainMapper;
26
27
    /**
28
     * @var \eZ\Publish\Core\Repository\Helper\LimitationService
29
     */
30
    private $limitationService;
31
32
    /**
33
     * @var \eZ\Publish\SPI\Persistence\User\Handler
34
     */
35
    private $userHandler;
36
37
    /**
38
     * @param \eZ\Publish\Core\Repository\Helper\RoleDomainMapper $roleDomainMapper
39
     * @param \eZ\Publish\Core\Repository\Helper\LimitationService $limitationService
40
     * @param \eZ\Publish\SPI\Persistence\User\Handler $userHandler
41
     */
42
    public function __construct(
43
        RoleDomainMapper $roleDomainMapper,
44
        LimitationService $limitationService,
45
        UserHandler $userHandler
46
    ) {
47
        $this->roleDomainMapper = $roleDomainMapper;
48
        $this->limitationService = $limitationService;
49
        $this->userHandler = $userHandler;
50
    }
51
52
    /**
53
     * todo
54
     *
55
     * @param string $module
56
     * @param string $function
57
     * @param \eZ\Publish\Core\Repository\PermissionResolver\Permission[] $permissions
58
     * @param \eZ\Publish\API\Repository\Values\ValueObject $object
59
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $userReference
60
     * @param array $targets
61
     *
62
     * @return bool
63
     */
64
    public function resolvePermissions(
65
        $module,
66
        $function,
67
        $permissions,
68
        ValueObject $object,
69
        UserReference $userReference,
70
        array $targets = []
71
    ) {
72
        foreach ($permissions as $permission) {
73
            $access = $this->resolvePermission(
74
                $module,
75
                $function,
76
                $permission,
77
                $object,
78
                $userReference,
79
                $targets
80
            );
81
82
            if ($access === true) {
83
                return true;
84
            }
85
        }
86
87
        return false;
88
    }
89
90
    /**
91
     * @param string $module
92
     * @param string $function
93
     * @param \eZ\Publish\Core\Repository\PermissionResolver\Permission $permission
94
     * @param \eZ\Publish\API\Repository\Values\ValueObject $object
95
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $userReference
96
     * @param array $targets
97
     *
98
     * @return bool
99
     */
100
    public function resolvePermission(
101
        $module,
102
        $function,
103
        Permission $permission,
104
        ValueObject $object,
105
        UserReference $userReference,
106
        array $targets = []
107
    ) {
108
        if (empty($targets)) {
109
            $targets = null;
110
        }
111
112
        /**
113
         * First deal with Role limitation if any.
114
         *
115
         * Here we accept ACCESS_GRANTED and ACCESS_ABSTAIN, the latter in cases where $object and $targets
116
         * are not supported by limitation.
117
         *
118
         * @var \eZ\Publish\API\Repository\Values\User\Limitation[]
119
         */
120
        if ($permission->limitation instanceof Limitation) {
121
            $type = $this->limitationService->getLimitationType($permission->limitation->getIdentifier());
122
            $accessVote = $type->evaluate($permission->limitation, $userReference, $object, $targets);
0 ignored issues
show
Bug introduced by
It seems like $targets defined by parameter $targets on line 106 can also be of type array; however, eZ\Publish\SPI\Limitation\Type::evaluate() does only seem to accept null|array<integer,objec...ry\Values\ValueObject>>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
123
            if ($accessVote === LimitationType::ACCESS_DENIED) {
124
                return false;
125
            }
126
        }
127
128
        /**
129
         * Loop over all policies.
130
         *
131
         * These are already filtered by hasAccess and given hasAccess did not return boolean
132
         * there must be some, so only return true if one of them says yes.
133
         *
134
         * @var \eZ\Publish\API\Repository\Values\User\Policy $policy
135
         */
136
        foreach ($permission->policies as $policy) {
137
            if (!($policy->module === $module || $policy->module === '*')) {
138
                continue;
139
            }
140
141
            if (!($policy->function === $function || $policy->function === '*')) {
142
                continue;
143
            }
144
145
            $limitations = $policy->getLimitations();
146
147
            /*
148
             * Return true if policy gives full access (aka no limitations)
149
             */
150
            if ($limitations === '*') {
151
                return true;
152
            }
153
154
            /*
155
             * Loop over limitations, all must return ACCESS_GRANTED for policy to pass.
156
             * If limitations was empty array this means same as '*'
157
             */
158
            $limitationsPass = true;
159 View Code Duplication
            foreach ($limitations as $limitation) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
160
                $type = $this->limitationService->getLimitationType($limitation->getIdentifier());
161
                $accessVote = $type->evaluate($limitation, $userReference, $object, $targets);
0 ignored issues
show
Bug introduced by
It seems like $targets defined by parameter $targets on line 106 can also be of type array; however, eZ\Publish\SPI\Limitation\Type::evaluate() does only seem to accept null|array<integer,objec...ry\Values\ValueObject>>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
162
                /*
163
                 * For policy limitation atm only support ACCESS_GRANTED
164
                 *
165
                 * Reasoning: Right now, use of a policy limitation not valid for a policy is per definition a
166
                 * BadState. To reach this you would have to configure the "policyMap" wrongly, like using
167
                 * Node (Location) limitation on state/assign. So in this case Role Limitations will return
168
                 * ACCESS_ABSTAIN (== no access here), and other limitations will throw InvalidArgument above,
169
                 * both cases forcing dev to investigate to find miss configuration. This might be relaxed in
170
                 * the future if valid use cases for ACCESS_ABSTAIN on policy limitations becomes known.
171
                 */
172
                if ($accessVote !== LimitationType::ACCESS_GRANTED) {
173
                    $limitationsPass = false;
174
                    break;// Break to next policy, all limitations must pass
175
                }
176
            }
177
            if ($limitationsPass) {
178
                return true;
179
            }
180
        }
181
182
        return false;
183
    }
184
185
    /**
186
     * todo
187
     *
188
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $userReference
189
     * @param string $module
190
     * @param string $function
191
     *
192
     * @return \eZ\Publish\Core\Repository\PermissionResolver\Permission[]
193
     */
194
    public function getPermissions(UserReference $userReference, $module = '*', $function = '*')
195
    {
196
        // Uses SPI to avoid triggering permission checks in Role/User service
197
        $permissionSets = [];
198
        $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($userReference->getUserId(), true);
199
        foreach ($spiRoleAssignments as $spiRoleAssignment) {
200
            $permissionSet = ['limitation' => null, 'policies' => []];
201
202
            $spiRole = $this->userHandler->loadRole($spiRoleAssignment->roleId);
203
            foreach ($spiRole->policies as $spiPolicy) {
204
                if (!($spiPolicy->module === $module || $spiPolicy->module === '*' || $module === '*')) {
205
                    continue;
206
                }
207
208
                if (!($spiPolicy->function === $function || $spiPolicy->function === '*' || $function === '*')) {
209
                    continue;
210
                }
211
212
                $permissionSet['policies'][] = $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy);
213
            }
214
215 View Code Duplication
            if (!empty($permissionSet['policies'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216
                if ($spiRoleAssignment->limitationIdentifier !== null) {
217
                    $permissionSet['limitation'] = $this->limitationService
218
                        ->getLimitationType($spiRoleAssignment->limitationIdentifier)
219
                        ->buildValue($spiRoleAssignment->values);
0 ignored issues
show
Bug introduced by
It seems like $spiRoleAssignment->values can also be of type null; however, eZ\Publish\SPI\Limitation\Type::buildValue() does only seem to accept array<integer,*>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
220
                }
221
222
                $permissionSets[] = new Permission($permissionSet);
223
            }
224
        }
225
226
        return $permissionSets;
227
    }
228
}
229