These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Kunstmaan\AdminBundle\Helper\Security\Acl\Permission; |
||
4 | |||
5 | use Doctrine\ORM\EntityManager; |
||
6 | use Kunstmaan\AdminBundle\Entity\AbstractEntity; |
||
7 | use Kunstmaan\AdminBundle\Entity\AclChangeset; |
||
8 | use Kunstmaan\AdminBundle\Entity\Role; |
||
9 | use Kunstmaan\UtilitiesBundle\Helper\Shell\Shell; |
||
10 | use Symfony\Component\EventDispatcher\EventDispatcherInterface; |
||
11 | use Symfony\Component\HttpFoundation\Request; |
||
12 | use Symfony\Component\HttpKernel\KernelInterface; |
||
13 | use Symfony\Component\Security\Acl\Domain\RoleSecurityIdentity; |
||
14 | use Symfony\Component\Security\Acl\Exception\AclNotFoundException; |
||
15 | use Symfony\Component\Security\Acl\Model\AclInterface; |
||
16 | use Symfony\Component\Security\Acl\Model\AclProviderInterface; |
||
17 | use Symfony\Component\Security\Acl\Model\AuditableEntryInterface; |
||
18 | use Symfony\Component\Security\Acl\Model\MutableAclInterface; |
||
19 | use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface; |
||
20 | use Symfony\Component\Security\Acl\Model\ObjectIdentityRetrievalStrategyInterface; |
||
21 | use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; |
||
22 | use Symfony\Component\Security\Core\Role\RoleInterface; |
||
23 | use Symfony\Component\Security\Core\User\UserInterface; |
||
24 | |||
25 | /** |
||
26 | * Helper to manage the permissions on a certain entity |
||
27 | */ |
||
28 | class PermissionAdmin |
||
29 | { |
||
30 | const ADD = 'ADD'; |
||
31 | const DELETE = 'DEL'; |
||
32 | |||
33 | /** |
||
34 | * @var AbstractEntity |
||
35 | */ |
||
36 | protected $resource = null; |
||
37 | |||
38 | /** |
||
39 | * @var EntityManager |
||
40 | */ |
||
41 | protected $em = null; |
||
42 | |||
43 | /** |
||
44 | * @var TokenStorageInterface |
||
45 | */ |
||
46 | protected $tokenStorage = null; |
||
47 | |||
48 | /** |
||
49 | * @var MutableAclProviderInterface |
||
50 | */ |
||
51 | protected $aclProvider = null; |
||
52 | |||
53 | /** |
||
54 | * @var ObjectIdentityRetrievalStrategyInterface |
||
55 | */ |
||
56 | protected $oidRetrievalStrategy = null; |
||
57 | |||
58 | /** |
||
59 | * @var PermissionMap |
||
60 | */ |
||
61 | protected $permissionMap = null; |
||
62 | |||
63 | /** |
||
64 | * @var array |
||
65 | */ |
||
66 | protected $permissions = null; |
||
67 | |||
68 | /** |
||
69 | * @var EventDispatcherInterface |
||
70 | */ |
||
71 | protected $eventDispatcher = null; |
||
72 | |||
73 | /** |
||
74 | * @var KernelInterface |
||
75 | */ |
||
76 | protected $kernel; |
||
77 | |||
78 | /** |
||
79 | * @var Shell |
||
80 | */ |
||
81 | protected $shellHelper; |
||
82 | |||
83 | /** |
||
84 | * Constructor |
||
85 | * |
||
86 | * @param EntityManager $em The EntityManager |
||
87 | * @param TokenStorageInterface $tokenStorage The token storage |
||
88 | * @param AclProviderInterface $aclProvider The ACL provider |
||
89 | * @param ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy The object retrieval strategy |
||
90 | * @param EventDispatcherInterface $eventDispatcher The event dispatcher |
||
91 | * @param Shell $shellHelper The shell helper |
||
92 | * @param KernelInterface $kernel The kernel |
||
93 | */ |
||
94 | 14 | public function __construct( |
|
95 | EntityManager $em, |
||
0 ignored issues
–
show
|
|||
96 | TokenStorageInterface $tokenStorage, |
||
97 | AclProviderInterface $aclProvider, |
||
98 | ObjectIdentityRetrievalStrategyInterface $oidRetrievalStrategy, |
||
99 | EventDispatcherInterface $eventDispatcher, |
||
100 | Shell $shellHelper, |
||
101 | KernelInterface $kernel |
||
102 | ) { |
||
103 | 14 | $this->em = $em; |
|
104 | 14 | $this->tokenStorage = $tokenStorage; |
|
105 | 14 | $this->aclProvider = $aclProvider; |
|
0 ignored issues
–
show
$aclProvider is of type object<Symfony\Component...l\AclProviderInterface> , but the property $aclProvider was declared to be of type object<Symfony\Component...leAclProviderInterface> . Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly. Either this assignment is in error or an instanceof check should be added for that assignment. class Alien {}
class Dalek extends Alien {}
class Plot
{
/** @var Dalek */
public $villain;
}
$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
$plot->villain = $alien;
}
Loading history...
|
|||
106 | 14 | $this->oidRetrievalStrategy = $oidRetrievalStrategy; |
|
107 | 14 | $this->eventDispatcher = $eventDispatcher; |
|
108 | 14 | $this->shellHelper = $shellHelper; |
|
109 | 14 | $this->kernel = $kernel; |
|
110 | 14 | } |
|
111 | |||
112 | /** |
||
113 | * Initialize permission admin with specified entity. |
||
114 | * |
||
115 | * @param AbstractEntity $resource The object which has the permissions |
||
116 | * @param PermissionMapInterface $permissionMap The permission map to use |
||
117 | */ |
||
118 | 12 | public function initialize(AbstractEntity $resource, PermissionMapInterface $permissionMap) |
|
119 | { |
||
120 | 12 | $this->resource = $resource; |
|
121 | 12 | $this->permissionMap = $permissionMap; |
|
0 ignored issues
–
show
$permissionMap is of type object<Kunstmaan\AdminBu...PermissionMapInterface> , but the property $permissionMap was declared to be of type object<Kunstmaan\AdminBu...rmission\PermissionMap> . Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly. Either this assignment is in error or an instanceof check should be added for that assignment. class Alien {}
class Dalek extends Alien {}
class Plot
{
/** @var Dalek */
public $villain;
}
$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
$plot->villain = $alien;
}
Loading history...
|
|||
122 | 12 | $this->permissions = array(); |
|
123 | |||
124 | // Init permissions |
||
125 | try { |
||
126 | 12 | $objectIdentity = $this->oidRetrievalStrategy->getObjectIdentity($this->resource); |
|
127 | /* @var $acl AclInterface */ |
||
128 | 11 | $acl = $this->aclProvider->findAcl($objectIdentity); |
|
129 | 11 | $objectAces = $acl->getObjectAces(); |
|
130 | /* @var $ace AuditableEntryInterface */ |
||
131 | 11 | View Code Duplication | foreach ($objectAces as $ace) { |
0 ignored issues
–
show
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...
|
|||
132 | 11 | $securityIdentity = $ace->getSecurityIdentity(); |
|
133 | 11 | if ($securityIdentity instanceof RoleSecurityIdentity) { |
|
134 | 11 | $this->permissions[$securityIdentity->getRole()] = new MaskBuilder($ace->getMask()); |
|
135 | } |
||
136 | } |
||
137 | 1 | } catch (AclNotFoundException $e) { |
|
138 | // No Acl found - do nothing (or should we initialize with default values here?) |
||
139 | } |
||
140 | 12 | } |
|
141 | |||
142 | /** |
||
143 | * Get permissions. |
||
144 | * |
||
145 | * @return MaskBuilder[] |
||
146 | */ |
||
147 | 1 | public function getPermissions() |
|
148 | { |
||
149 | 1 | return $this->permissions; |
|
150 | } |
||
151 | |||
152 | /** |
||
153 | * Get permission for specified role. |
||
154 | * |
||
155 | * @param RoleInterface|string $role |
||
156 | * |
||
157 | * @return MaskBuilder|null |
||
158 | */ |
||
159 | 3 | public function getPermission($role) |
|
160 | { |
||
161 | 3 | if ($role instanceof RoleInterface || $role instanceof \Symfony\Component\Security\Core\Role\Role) { |
|
162 | 1 | $role = $role->getRole(); |
|
163 | } |
||
164 | 3 | if (isset($this->permissions[$role])) { |
|
165 | 2 | return $this->permissions[$role]; |
|
166 | } |
||
167 | |||
168 | 1 | return null; |
|
169 | } |
||
170 | |||
171 | /** |
||
172 | * Get all roles. |
||
173 | * |
||
174 | * @return Role[] |
||
0 ignored issues
–
show
|
|||
175 | */ |
||
176 | 1 | public function getAllRoles() |
|
177 | { |
||
178 | 1 | return $this->em->getRepository(Role::class)->findAll(); |
|
179 | } |
||
180 | |||
181 | /** |
||
182 | * Get all manageable roles for pages |
||
183 | * |
||
184 | * @return Role[] |
||
185 | */ |
||
186 | 1 | public function getManageableRolesForPages() |
|
187 | { |
||
188 | 1 | $roles = $this->em->getRepository(Role::class)->findAll(); |
|
189 | |||
190 | 1 | if (($token = $this->tokenStorage->getToken()) && ($user = $token->getUser())) { |
|
191 | 1 | if ($user && !$user->isSuperAdmin() && ($superAdminRole = array_keys($roles, 'ROLE_SUPER_ADMIN'))) { |
|
192 | 1 | $superAdminRole = current($superAdminRole); |
|
193 | 1 | unset($roles[$superAdminRole]); |
|
194 | } |
||
195 | } |
||
196 | |||
197 | 1 | return $roles; |
|
198 | } |
||
199 | |||
200 | /** |
||
201 | * Get possible permissions. |
||
202 | * |
||
203 | * @return array |
||
0 ignored issues
–
show
|
|||
204 | */ |
||
205 | 1 | public function getPossiblePermissions() |
|
206 | { |
||
207 | 1 | return $this->permissionMap->getPossiblePermissions(); |
|
208 | } |
||
209 | |||
210 | /** |
||
211 | * Handle form entry of permission changes. |
||
212 | * |
||
213 | * @param Request $request |
||
214 | * |
||
215 | * @return bool |
||
216 | */ |
||
217 | 2 | public function bindRequest(Request $request) |
|
218 | { |
||
219 | 2 | $changes = $request->request->get('permission-hidden-fields'); |
|
220 | |||
221 | 2 | if (empty($changes)) { |
|
222 | 1 | return true; |
|
223 | } |
||
224 | |||
225 | // Just apply the changes to the current node (non recursively) |
||
226 | 1 | $this->applyAclChangeset($this->resource, $changes, false); |
|
227 | |||
228 | // Apply recursively (on request) |
||
229 | 1 | $applyRecursive = $request->request->get('applyRecursive'); |
|
230 | 1 | if ($applyRecursive) { |
|
231 | // Serialize changes & store them in DB |
||
232 | 1 | $user = $this->tokenStorage->getToken()->getUser(); |
|
233 | 1 | $this->createAclChangeSet($this->resource, $changes, $user); |
|
234 | |||
235 | 1 | $cmd = 'php ' . $this->kernel->getRootDir() . '/../bin/console kuma:acl:apply'; |
|
236 | 1 | $cmd .= ' --env=' . $this->kernel->getEnvironment(); |
|
237 | |||
238 | 1 | $this->shellHelper->runInBackground($cmd); |
|
239 | } |
||
240 | |||
241 | 1 | return true; |
|
242 | } |
||
243 | |||
244 | /** |
||
245 | * Create a new ACL changeset. |
||
246 | * |
||
247 | * @param AbstractEntity $entity The entity |
||
248 | * @param array $changes The changes |
||
249 | * @param UserInterface $user The user |
||
250 | * |
||
251 | * @return AclChangeset |
||
252 | */ |
||
253 | 2 | public function createAclChangeSet(AbstractEntity $entity, $changes, UserInterface $user) |
|
254 | { |
||
255 | 2 | $aclChangeset = new AclChangeset(); |
|
256 | 2 | $aclChangeset->setRef($entity); |
|
257 | 2 | $aclChangeset->setChangeset($changes); |
|
258 | /* @var $user BaseUser */ |
||
259 | 2 | $aclChangeset->setUser($user); |
|
260 | 2 | $this->em->persist($aclChangeset); |
|
261 | 2 | $this->em->flush(); |
|
262 | |||
263 | 2 | return $aclChangeset; |
|
264 | } |
||
265 | |||
266 | /** |
||
267 | * Apply the specified ACL changeset. |
||
268 | * |
||
269 | * @param AbstractEntity $entity The entity |
||
270 | * @param array $changeset The changeset |
||
271 | * @param bool $recursive The recursive |
||
272 | */ |
||
273 | 4 | public function applyAclChangeset(AbstractEntity $entity, $changeset, $recursive = true) |
|
274 | { |
||
275 | 4 | if ($recursive) { |
|
276 | 2 | if (!method_exists($entity, 'getChildren')) { |
|
277 | 1 | return; |
|
278 | } |
||
279 | |||
280 | // Iterate over children and apply recursively |
||
281 | 1 | foreach ($entity->getChildren() as $child) { |
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
Kunstmaan\AdminBundle\Entity\AbstractEntity as the method getChildren() does only exist in the following sub-classes of Kunstmaan\AdminBundle\Entity\AbstractEntity : Kunstmaan\MediaBundle\Entity\Folder , Kunstmaan\MenuBundle\Entity\MenuItem , Kunstmaan\NodeBundle\Entity\Node . Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
Loading history...
|
|||
282 | 1 | $this->applyAclChangeset($child, $changeset); |
|
283 | } |
||
284 | } |
||
285 | |||
286 | // Apply ACL modifications to node |
||
287 | 3 | $objectIdentity = $this->oidRetrievalStrategy->getObjectIdentity($entity); |
|
288 | |||
289 | try { |
||
290 | /* @var $acl MutableAclInterface */ |
||
291 | 3 | $acl = $this->aclProvider->findAcl($objectIdentity); |
|
292 | 1 | } catch (AclNotFoundException $e) { |
|
293 | /* @var $acl MutableAclInterface */ |
||
294 | 1 | $acl = $this->aclProvider->createAcl($objectIdentity); |
|
295 | } |
||
296 | |||
297 | // Process permissions in changeset |
||
298 | 3 | foreach ($changeset as $role => $roleChanges) { |
|
299 | 2 | $index = $this->getObjectAceIndex($acl, $role); |
|
300 | 2 | $mask = 0; |
|
301 | 2 | if (false !== $index) { |
|
302 | 1 | $mask = $this->getMaskAtIndex($acl, $index); |
|
303 | } |
||
304 | 2 | foreach ($roleChanges as $type => $permissions) { |
|
305 | 2 | $maskChange = new MaskBuilder(); |
|
306 | 2 | foreach ($permissions as $permission) { |
|
307 | 2 | $maskChange->add($permission); |
|
308 | } |
||
309 | switch ($type) { |
||
310 | 2 | case self::ADD: |
|
311 | 1 | $mask |= $maskChange->get(); |
|
312 | |||
313 | 1 | break; |
|
314 | 1 | case self::DELETE: |
|
315 | 1 | $mask &= ~$maskChange->get(); |
|
316 | |||
317 | 1 | break; |
|
318 | } |
||
319 | } |
||
320 | 2 | if (false !== $index) { |
|
321 | 1 | $acl->updateObjectAce($index, $mask); |
|
0 ignored issues
–
show
It seems like
$mask defined by $this->getMaskAtIndex($acl, $index) on line 302 can also be of type false ; however, Symfony\Component\Securi...face::updateObjectAce() does only seem to accept integer , did you maybe forget to handle an error condition?
This check looks for type mismatches where the missing type is Consider the follow example <?php
function getDate($date)
{
if ($date !== null) {
return new DateTime($date);
}
return false;
}
This function either returns a new
Loading history...
|
|||
322 | } else { |
||
323 | 1 | $securityIdentity = new RoleSecurityIdentity($role); |
|
324 | 1 | $acl->insertObjectAce($securityIdentity, $mask); |
|
0 ignored issues
–
show
It seems like
$mask defined by $this->getMaskAtIndex($acl, $index) on line 302 can also be of type false ; however, Symfony\Component\Securi...face::insertObjectAce() does only seem to accept integer , did you maybe forget to handle an error condition?
This check looks for type mismatches where the missing type is Consider the follow example <?php
function getDate($date)
{
if ($date !== null) {
return new DateTime($date);
}
return false;
}
This function either returns a new
Loading history...
|
|||
325 | } |
||
326 | } |
||
327 | 3 | $this->aclProvider->updateAcl($acl); |
|
328 | 3 | } |
|
329 | |||
330 | /** |
||
331 | * Get current object ACE index for specified role. |
||
332 | * |
||
333 | * @param AclInterface $acl The AclInterface |
||
334 | * @param string $role The role |
||
335 | * |
||
336 | * @return bool|int |
||
0 ignored issues
–
show
|
|||
337 | */ |
||
338 | 2 | View Code Duplication | private function getObjectAceIndex(AclInterface $acl, $role) |
0 ignored issues
–
show
This method seems to be duplicated in 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...
|
|||
339 | { |
||
340 | 2 | $objectAces = $acl->getObjectAces(); |
|
341 | /* @var $ace AuditableEntryInterface */ |
||
342 | 2 | foreach ($objectAces as $index => $ace) { |
|
343 | 2 | $securityIdentity = $ace->getSecurityIdentity(); |
|
344 | 2 | if (($securityIdentity instanceof RoleSecurityIdentity) && $securityIdentity->getRole() == $role) { |
|
345 | 1 | return $index; |
|
346 | } |
||
347 | } |
||
348 | |||
349 | 1 | return false; |
|
350 | } |
||
351 | |||
352 | /** |
||
353 | * Get object ACE mask at specified index. |
||
354 | * |
||
355 | * @param AclInterface $acl The acl interface |
||
356 | * @param int $index The index |
||
357 | * |
||
358 | * @return bool|int |
||
0 ignored issues
–
show
|
|||
359 | */ |
||
360 | 2 | View Code Duplication | private function getMaskAtIndex(AclInterface $acl, $index) |
0 ignored issues
–
show
This method seems to be duplicated in 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...
|
|||
361 | { |
||
362 | 2 | $objectAces = $acl->getObjectAces(); |
|
363 | /* @var $ace AuditableEntryInterface */ |
||
364 | 2 | $ace = $objectAces[$index]; |
|
365 | 2 | $securityIdentity = $ace->getSecurityIdentity(); |
|
366 | 2 | if ($securityIdentity instanceof RoleSecurityIdentity) { |
|
367 | 2 | return $ace->getMask(); |
|
368 | } |
||
369 | |||
370 | 1 | return false; |
|
371 | } |
||
372 | } |
||
373 |
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.