AclPrivilegeRepository::getPermissionNames()   B
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 19
rs 8.8571
cc 5
eloc 11
nc 5
nop 1
1
<?php
2
3
namespace Oro\Bundle\SecurityBundle\Acl\Persistence;
4
5
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
6
use Symfony\Component\Security\Acl\Exception\NotAllAclsFoundException;
7
use Symfony\Component\Security\Acl\Model\SecurityIdentityInterface as SID;
8
use Symfony\Component\Security\Acl\Domain\ObjectIdentity as OID;
9
use Symfony\Component\Security\Acl\Model\EntryInterface;
10
use Symfony\Component\Security\Acl\Model\AclInterface;
11
use Symfony\Component\Translation\TranslatorInterface;
12
13
use Doctrine\Common\Collections\ArrayCollection;
14
15
use Oro\Bundle\SecurityBundle\Acl\Domain\ObjectIdentityFactory;
16
use Oro\Bundle\SecurityBundle\Acl\AccessLevel;
17
use Oro\Bundle\SecurityBundle\Acl\Permission\MaskBuilder;
18
use Oro\Bundle\SecurityBundle\Acl\Extension\AclExtensionInterface;
19
use Oro\Bundle\SecurityBundle\Model\AclPrivilege;
20
use Oro\Bundle\SecurityBundle\Model\AclPrivilegeIdentity;
21
use Oro\Bundle\SecurityBundle\Model\AclPermission;
22
use Oro\Bundle\SecurityBundle\Acl\Extension\AclClassInfo;
23
24
/**
25
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
26
 */
27
class AclPrivilegeRepository
28
{
29
    const ROOT_PRIVILEGE_NAME = '(default)';
30
31
    /**
32
     * @var AclManager
33
     */
34
    protected $manager;
35
36
    /**
37
     * @var TranslatorInterface
38
     */
39
    protected $translator;
40
41
    /**
42
     * @param AclManager $manager
43
     * @param TranslatorInterface $translator
44
     */
45
    final public function __construct(AclManager $manager, TranslatorInterface $translator)
46
    {
47
        $this->manager = $manager;
48
        $this->translator = $translator;
49
    }
50
51
    /**
52
     * Gets a list of all permission names supported by ACL extension which is responsible
53
     * to process domain objects of the given type(s).
54
     * In case when $extensionKeyOrKeys argument contains several keys this method returns
55
     * unique combination of all permission names supported by the requested ACL extensions.
56
     * For example if one ACL extension supports VIEW, CREATE and EDIT permissions
57
     * and another ACL extension supports VIEW and DELETE permissions,
58
     * the result will be: VIEW, CREATE, EDIT, DELETE
59
     *
60
     * @param string|string[] $extensionKeyOrKeys The ACL extension key(s)
61
     * @return string[]
62
     */
63
    public function getPermissionNames($extensionKeyOrKeys)
64
    {
65
        if (is_string($extensionKeyOrKeys)) {
66
            return $this->manager->getExtensionSelector()->select($this->manager->getRootOid($extensionKeyOrKeys))
67
                ->getPermissions();
68
        }
69
70
        $result = array();
71
        foreach ($extensionKeyOrKeys as $extensionKey) {
72
            $extension = $this->manager->getExtensionSelector()->select($this->manager->getRootOid($extensionKey));
73
            foreach ($extension->getPermissions() as $permission) {
74
                if (!in_array($permission, $result)) {
75
                    $result[] = $permission;
76
                }
77
            }
78
        }
79
80
        return $result;
81
    }
82
83
    /**
84
     * Gets all privileges associated with the given security identity.
85
     *
86
     * @param SID $sid
87
     * @return ArrayCollection|AclPrivilege[]
88
     */
89
    public function getPrivileges(SID $sid)
90
    {
91
        $privileges = new ArrayCollection();
92
        foreach ($this->manager->getAllExtensions() as $extension) {
93
            $extensionKey = $extension->getExtensionKey();
94
95
            // fill a list of object identities;
96
            // the root object identity is added to the top of the list (for performance reasons)
97
            /** @var OID[] $oids */
98
            $classes = array();
99
            $oids = array();
100
            foreach ($extension->getClasses() as $class) {
101
                $className = $class->getClassName();
102
                $oids[] = new OID($extensionKey, $className);
103
                $classes[$className] = $class;
104
            }
105
            $rootOid = $this->manager->getRootOid($extensionKey);
106
            array_unshift($oids, $rootOid);
107
108
            // load ACLs for all object identities
109
            $acls = $this->findAcls($sid, $oids);
110
            // find ACL for the root object identity
111
            $rootAcl = $this->findAclByOid($acls, $rootOid);
0 ignored issues
show
Bug introduced by
It seems like $acls defined by $this->findAcls($sid, $oids) on line 109 can be null; however, Oro\Bundle\SecurityBundl...ository::findAclByOid() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
112
113
            foreach ($oids as $oid) {
114
                if ($oid->getType() === ObjectIdentityFactory::ROOT_IDENTITY_TYPE) {
115
                    $name = self::ROOT_PRIVILEGE_NAME;
116
                    $group = '';
117
                    $description = '';
118
                } else {
119
                    /** @var AclClassInfo $class */
120
                    $class = $classes[$oid->getType()];
121
                    $name = $class->getLabel();
122
                    if (empty($name)) {
123
                        $name = substr($class->getClassName(), strpos($class->getClassName(), '\\'));
124
                    }
125
                    $group = $class->getGroup();
126
                    $description = $class->getDescription();
127
                }
128
129
                $privilege = new AclPrivilege();
130
                $privilege
131
                    ->setIdentity(
132
                        new AclPrivilegeIdentity(
133
                            $oid->getIdentifier() . ':' . $oid->getType(),
134
                            $name
135
                        )
136
                    )
137
                    ->setGroup($group)
138
                    ->setExtensionKey($extensionKey)
139
                    ->setDescription($description);
140
141
                $this->addPermissions($sid, $privilege, $oid, $acls, $extension, $rootAcl);
0 ignored issues
show
Bug introduced by
It seems like $acls defined by $this->findAcls($sid, $oids) on line 109 can be null; however, Oro\Bundle\SecurityBundl...itory::addPermissions() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
142
143
                $privileges->add($privilege);
144
            }
145
        }
146
147
        $this->sortPrivileges($privileges);
148
149
        return $privileges;
150
    }
151
152
    /**
153
     * Associates privileges with the given security identity.
154
     *
155
     * @param SID $sid
156
     * @param ArrayCollection|AclPrivilege[] $privileges
157
     * @throws \RuntimeException
158
     *
159
     * @SuppressWarnings(PHPMD.NPathComplexity)
160
     */
161
    public function savePrivileges(SID $sid, ArrayCollection $privileges)
162
    {
163
        /**
164
         * @var $rootKeys
165
         * key = ExtensionKey
166
         * value = a key in $privilege collection
167
         */
168
        $rootKeys = array();
169
        // find all root privileges
170
        foreach ($privileges as $key => $privilege) {
171
            $identity = $privilege->getIdentity()->getId();
172
            if (strpos($identity, ObjectIdentityFactory::ROOT_IDENTITY_TYPE)) {
173
                $extensionKey = substr($identity, 0, strpos($identity, ':'));
174
                $rootKeys[$extensionKey] = $key;
175
            }
176
        }
177
178
        /**
179
         * @var $context
180
         * key = ExtensionKey
181
         * value = array
182
         *      'extension' => extension
183
         *      'maskBuilders' => array
184
         *              key = permission name
185
         *              value = MaskBuilder (the same instance for all permissions supported by the builder)
186
         *      'rootMasks' => array of integer
187
         */
188
        // init the context
189
        $context = array();
190
        $this->initSaveContext($context, $rootKeys, $sid, $privileges);
191
192
        // set permissions for all root objects and remove all root privileges from $privileges collection
193
        foreach ($context as $extensionKey => $contextItem) {
194
            /** @var AclExtensionInterface $extension */
195
            $extension = $contextItem['extension'];
196
            if (isset($rootKeys[$extensionKey])) {
197
                $privilegeKey = $rootKeys[$extensionKey];
198
                $privilege = $privileges[$privilegeKey];
199
                unset($privileges[$privilegeKey]);
200
                $identity = $privilege->getIdentity()->getId();
201
                $oid = $extension->getObjectIdentity($identity);
202
            } else {
203
                $oid = $this->manager->getRootOid($extensionKey);
204
            }
205
            $rootMasks = $context[$extensionKey]['rootMasks'];
206
            foreach ($rootMasks as $mask) {
207
                $this->manager->setPermission($sid, $oid, $mask);
208
            }
209
        }
210
211
        // set permissions for other objects
212
        foreach ($privileges as $privilege) {
213
            $identity = $privilege->getIdentity()->getId();
214
            $extensionKey = substr($identity, 0, strpos($identity, ':'));
215
            /** @var AclExtensionInterface $extension */
216
            $extension = $context[$extensionKey]['extension'];
217
            $oid = $extension->getObjectIdentity($identity);
218
            $maskBuilders = $context[$extensionKey]['maskBuilders'];
219
            $masks = $this->getPermissionMasks($privilege->getPermissions(), $extension, $maskBuilders);
220
            $rootMasks = $context[$extensionKey]['rootMasks'];
221
            foreach ($this->manager->getAces($sid, $oid) as $ace) {
222
                if (!$ace->isGranting()) {
223
                    // denying ACE is not supported
224
                    continue;
225
                }
226
                $mask = $this->updateExistingPermissions($sid, $oid, $ace->getMask(), $masks, $rootMasks, $extension);
227
                // as we have already processed $mask, remove it from $masks collection
228
                if ($mask !== false) {
229
                    $this->removeMask($masks, $mask);
230
                }
231
            }
232
            // check if we have new masks so far, and process them if any
233
            foreach ($masks as $mask) {
234
                $rootMask = $this->findSimilarMask($rootMasks, $mask, $extension);
235
                if ($rootMask === false || $mask !== $extension->adaptRootMask($rootMask, $oid)) {
236
                    $this->manager->setPermission($sid, $oid, $mask);
237
                }
238
            }
239
        }
240
241
        $this->manager->flush();
242
    }
243
244
    /**
245
     * Prepares the context is used in savePrivileges method
246
     *
247
     * @param array $context
248
     * @param array $rootKeys
249
     * @param SID $sid
250
     * @param ArrayCollection|AclPrivilege[] $privileges
251
     */
252
    protected function initSaveContext(array &$context, array $rootKeys, SID $sid, ArrayCollection $privileges)
253
    {
254
        foreach ($this->manager->getAllExtensions() as $extension) {
255
            $extensionKey = $extension->getExtensionKey();
256
            /** @var MaskBuilder[] $maskBuilders */
257
            $maskBuilders = array();
258
            $this->prepareMaskBuilders($maskBuilders, $extension);
259
            $context[$extensionKey] = array(
260
                'extension' => $extension,
261
                'maskBuilders' => $maskBuilders
262
            );
263
            if (isset($rootKeys[$extensionKey])) {
264
                $privilege = $privileges[$rootKeys[$extensionKey]];
265
                $rootMasks = $this->getPermissionMasks($privilege->getPermissions(), $extension, $maskBuilders);
266
            } else {
267
                $rootMasks = array();
268
                $oid = $this->manager->getRootOid($extension->getExtensionKey());
269
                foreach ($this->manager->getAces($sid, $oid) as $ace) {
270
                    if (!$ace->isGranting()) {
271
                        // denying ACE is not supported
272
                        continue;
273
                    }
274
                    $rootMasks[] = $ace->getMask();
275
                }
276
                // add missing masks
277
                foreach ($extension->getAllMaskBuilders() as $maskBuilder) {
278
                    $emptyMask = $maskBuilder->get();
279
                    $maskAlreadyExist = false;
280
                    foreach ($rootMasks as $rootMask) {
281
                        if ($extension->getServiceBits($emptyMask) === $extension->getServiceBits($rootMask)) {
282
                            $maskAlreadyExist = true;
283
                            break;
284
                        }
285
                    }
286
                    if (!$maskAlreadyExist) {
287
                        $rootMasks[] = $emptyMask;
288
                    }
289
                }
290
            }
291
            $context[$extensionKey]['rootMasks'] = $rootMasks;
292
        }
293
    }
294
295
    /**
296
     * Fills an associative array is used to find correct mask builder by the a permission name
297
     *
298
     * @param MaskBuilder[] $maskBuilders [output]
299
     * @param AclExtensionInterface $extension
300
     */
301
    protected function prepareMaskBuilders(array &$maskBuilders, AclExtensionInterface $extension)
302
    {
303
        foreach ($extension->getPermissions() as $permission) {
304
            $maskBuilder = $extension->getMaskBuilder($permission);
305
            foreach ($maskBuilders as $mb) {
306
                if ($mb->get() === $maskBuilder->get()) {
307
                    $maskBuilder = $mb;
308
                    break;
309
                }
310
            }
311
            $maskBuilders[$permission] = $maskBuilder;
312
        }
313
    }
314
315
    /**
316
     * Makes necessary modifications for existing ACE
317
     *
318
     * @param SID $sid
319
     * @param OID $oid
320
     * @param int $existingMask
321
     * @param int[] $masks [input/output]
322
     * @param int[] $rootMasks
323
     * @param AclExtensionInterface $extension
324
     * @return bool|int The mask if it was processed, otherwise, false
325
     */
326
    protected function updateExistingPermissions(
327
        SID $sid,
328
        OID $oid,
329
        $existingMask,
330
        $masks,
331
        $rootMasks,
332
        AclExtensionInterface $extension
333
    ) {
334
        $mask = $this->findSimilarMask($masks, $existingMask, $extension);
335
        $rootMask = $this->findSimilarMask($rootMasks, $existingMask, $extension);
336
        if ($mask === false && $rootMask === false) {
337
            // keep existing ACE as is, because both $mask and $rootMask were not found
338
        } elseif ($rootMask === false) {
339
            // if $rootMask was not found, just update existing ACE using $mask
340
            $this->manager->setPermission($sid, $oid, $mask);
0 ignored issues
show
Security Bug introduced by
It seems like $mask defined by $this->findSimilarMask($...istingMask, $extension) on line 334 can also be of type false; however, Oro\Bundle\SecurityBundl...anager::setPermission() 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 false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
341
        } elseif ($mask === false) {
342
            // if $mask was not found, use $rootMask to check
343
            // whether existing ACE need to be removed or keep as is
344
            if ($existingMask === $extension->adaptRootMask($rootMask, $oid)) {
345
                // remove existing ACE because it provides the same permissions as the root ACE
346
                $this->manager->deletePermission($sid, $oid, $existingMask);
347
            }
348
        } else {
349
            // both $mask and $rootMask were found
350
            if ($mask === $extension->adaptRootMask($rootMask, $oid)) {
351
                // remove existing ACE, if $mask provides the same permissions as $rootMask
352
                $this->manager->deletePermission($sid, $oid, $existingMask);
353
            } else {
354
                // update existing ACE using $mask, if permissions provide by $mask and $rootMask are different
355
                $this->manager->setPermission($sid, $oid, $mask);
356
            }
357
        }
358
359
        return $mask;
360
    }
361
362
    /**
363
     * Finds a mask from $masks array with the same "service bits" as in $needleMask
364
     *
365
     * @param int[] $masks
366
     * @param int $needleMask
367
     * @param AclExtensionInterface $extension
368
     * @return int|bool The found mask, or false if a mask was not found in $masks
369
     */
370
    protected function findSimilarMask(array $masks, $needleMask, AclExtensionInterface $extension)
371
    {
372
        foreach ($masks as $mask) {
373
            if ($extension->getServiceBits($needleMask) === $extension->getServiceBits($mask)) {
374
                return $mask;
375
            }
376
        }
377
378
        return false;
379
    }
380
381
    /**
382
     * Removes $needleMask mask from $masks array
383
     *
384
     * @param int[] $masks [input/output]
385
     * @param int $needleMask
386
     */
387
    protected function removeMask(array &$masks, $needleMask)
388
    {
389
        $keyToRemove = null;
390
        foreach ($masks as $key => $mask) {
391
            if ($mask === $needleMask) {
392
                $keyToRemove = $key;
393
                break;
394
            }
395
        }
396
        if ($keyToRemove !== null) {
397
            unset($masks[$keyToRemove]);
398
        }
399
    }
400
401
    /**
402
     * Gets a list of masks from permissions given in $permissions argument
403
     *
404
     * @param ArrayCollection|AclPermission[] $permissions
405
     * @param AclExtensionInterface $extension
406
     * @param MaskBuilder[] $maskBuilders
407
     * @return int[]
408
     */
409
    protected function getPermissionMasks($permissions, AclExtensionInterface $extension, array $maskBuilders)
410
    {
411
        $masks = array();
412
413
        foreach ($maskBuilders as $maskBuilder) {
414
            $maskBuilder->reset();
415
        }
416
417
        foreach ($permissions as $permission) {
418
            $maskBuilder = $maskBuilders[$permission->getName()];
419
            $accessLevelName = AccessLevel::getAccessLevelName($permission->getAccessLevel());
420
            if ($accessLevelName !== null) {
421
                $maskName = 'MASK_' . $permission->getName() . '_' . $accessLevelName;
422
                // check if a mask builder supports access levels
423
                if (!$maskBuilder->hasMask($maskName)) {
424
                    // remove access level name from the mask name if a mask builder do not support access levels
425
                    $maskName = 'MASK_' . $permission->getName();
426
                }
427
                $maskBuilder->add($maskBuilder->getMask($maskName));
428
            }
429
            $masks[$extension->getServiceBits($maskBuilder->get())] = $maskBuilder->get();
430
        }
431
432
        return array_values($masks);
433
    }
434
435
    /**
436
     * Gets ACLs for given object identities
437
     *
438
     * @param SID $sid
439
     * @param OID[] $oids
440
     * @return \SplObjectStorage
441
     */
442
    protected function findAcls(SID $sid, array $oids)
443
    {
444
        try {
445
            return $this->manager->findAcls($sid, $oids);
446
        } catch (NotAllAclsFoundException $partial) {
447
            return $partial->getPartialResult();
448
        }
449
    }
450
451
    /**
452
     * Sorts the given privileges by name in alphabetical order.
453
     * The root privilege is moved at the top of the list.
454
     *
455
     * @param ArrayCollection $privileges
456
     */
457
    protected function sortPrivileges(ArrayCollection $privileges)
458
    {
459
        $data = [];
460
        /** @var AclPrivilege $privilege */
461
        foreach ($privileges->getIterator() as $privilege) {
462
            $isRoot = false !== strpos($privilege->getIdentity()->getId(), ObjectIdentityFactory::ROOT_IDENTITY_TYPE);
463
            $label  = !$isRoot
464
                ? $this->translator->trans($privilege->getIdentity()->getName())
465
                : null;
466
467
            $data[] = [$privilege, $isRoot, $label];
468
        }
469
        uasort(
470
            $data,
471 View Code Duplication
            function ($a, $b) {
472
                if ($a[1]) {
473
                    return -1;
474
                }
475
                if ($b[1]) {
476
                    return 1;
477
                }
478
479
                return strcmp($a[2], $b[2]);
480
            }
481
        );
482
483
        $privileges->clear();
484
        foreach ($data as $item) {
485
            $privileges->add($item[0]);
486
        }
487
    }
488
489
    /**
490
     * Gets ACL associated with the given object identity from the collections specified in $acls argument.
491
     *
492
     * @param \SplObjectStorage $acls
493
     * @param OID $oid
494
     * @return AclInterface|null
495
     */
496
    protected function findAclByOid(\SplObjectStorage $acls, ObjectIdentity $oid)
497
    {
498
        $result = null;
499
        foreach ($acls as $aclOid) {
500
            if ($oid->equals($aclOid)) {
501
                $result = $acls->offsetGet($aclOid);
502
                break;
503
            }
504
        }
505
506
        return $result;
507
    }
508
509
    /**
510
     * Adds permissions to the given $privilege.
511
     *
512
     * @param SID $sid
513
     * @param AclPrivilege $privilege
514
     * @param OID $oid
515
     * @param \SplObjectStorage $acls
516
     * @param AclExtensionInterface $extension
517
     * @param AclInterface $rootAcl
518
     */
519
    protected function addPermissions(
520
        SID $sid,
521
        AclPrivilege $privilege,
522
        OID $oid,
523
        \SplObjectStorage $acls,
524
        AclExtensionInterface $extension,
525
        AclInterface $rootAcl = null
526
    ) {
527
        $allowedPermissions = $extension->getAllowedPermissions($oid);
528
        $acl = $this->findAclByOid($acls, $oid);
529
        if ($rootAcl !== null) {
530
            $this->addAclPermissions($sid, null, $privilege, $allowedPermissions, $extension, $rootAcl, $acl);
531
        }
532
533
        foreach ($allowedPermissions as $permission) {
534
            if (!$privilege->hasPermission($permission)) {
535
                $privilege->addPermission(new AclPermission($permission, AccessLevel::NONE_LEVEL));
536
            }
537
        }
538
    }
539
540
    /**
541
     * Adds permissions to the given $privilege based on the given ACL.
542
     * The $permissions argument is used to filter privileges for the given permissions only.
543
     *
544
     * @param SID $sid
545
     * @param string|null $field The name of a field.
546
     *                           Set to null to work with class-based and object-based ACEs
547
     *                           Set to not null to work with class-field-based and object-field-based ACEs
548
     * @param AclPrivilege $privilege
549
     * @param string[] $permissions
550
     * @param AclExtensionInterface $extension
551
     * @param AclInterface $rootAcl
552
     * @param AclInterface $acl
553
     */
554
    protected function addAclPermissions(
555
        SID $sid,
556
        $field,
557
        AclPrivilege $privilege,
558
        array $permissions,
559
        AclExtensionInterface $extension,
560
        AclInterface $rootAcl,
561
        AclInterface $acl = null
562
    ) {
563
        if ($acl !== null) {
564
            // check object ACEs
565
            $this->addAcesPermissions(
566
                $privilege,
567
                $permissions,
568
                $this->getAces($sid, $acl, AclManager::OBJECT_ACE, $field),
569
                $extension
570
            );
571
            // check class ACEs if object ACEs were not contains all requested privileges
572 View Code Duplication
            if ($privilege->getPermissionCount() < count($permissions)) {
573
                $this->addAcesPermissions(
574
                    $privilege,
575
                    $permissions,
576
                    $this->getAces($sid, $acl, AclManager::CLASS_ACE, $field),
577
                    $extension
578
                );
579
            }
580
            // check parent ACEs if object and class ACEs were not contains all requested privileges
581
            if ($privilege->getPermissionCount() < count($permissions) && $acl->isEntriesInheriting()) {
582
                $parentAcl = $acl->getParentAcl();
583
                if ($parentAcl !== null) {
584
                    $this->addAclPermissions($sid, $field, $privilege, $permissions, $extension, $rootAcl, $parentAcl);
585
                }
586
            }
587
        }
588
        // if so far not all requested privileges are found get them from the root ACL
589 View Code Duplication
        if ($privilege->getPermissionCount() < count($permissions)) {
590
            $this->addAcesPermissions(
591
                $privilege,
592
                $permissions,
593
                $this->getAces($sid, $rootAcl, AclManager::OBJECT_ACE, $field),
594
                $extension,
595
                true
596
            );
597
        }
598
    }
599
600
    /**
601
     * Gets all ACEs associated with given ACL and the given security identity
602
     *
603
     * @param SID $sid
604
     * @param AclInterface $acl
605
     * @param string $type The ACE type. Can be one of AclManager::*_ACE constants
606
     * @param string|null $field The name of a field.
607
     *                           Set to null for class-based or object-based ACE
608
     *                           Set to not null class-field-based or object-field-based ACE
609
     * @return EntryInterface[]
610
     */
611
    protected function getAces(SID $sid, AclInterface $acl, $type, $field)
612
    {
613
        return array_filter(
614
            $this->manager->getAceProvider()->getAces($acl, $type, $field),
615
            function ($ace) use (&$sid) {
616
                /** @var EntryInterface $ace */
617
618
                return $sid->equals($ace->getSecurityIdentity());
619
            }
620
        );
621
    }
622
623
    /**
624
     * Adds permissions to the given $privilege based on the given ACEs.
625
     * The $permissions argument is used to filter privileges for the given permissions only.
626
     *
627
     * @param AclPrivilege $privilege
628
     * @param string[] $permissions
629
     * @param EntryInterface[] $aces
630
     * @param AclExtensionInterface $extension
631
     * @param bool $itIsRootAcl
632
     */
633
    protected function addAcesPermissions(
634
        AclPrivilege $privilege,
635
        array $permissions,
636
        array $aces,
637
        AclExtensionInterface $extension,
638
        $itIsRootAcl = false
639
    ) {
640
        if (empty($aces)) {
641
            return;
642
        }
643
        foreach ($aces as $ace) {
644
            if (!$ace->isGranting()) {
645
                // denying ACE is not supported
646
                continue;
647
            }
648
            $mask = $ace->getMask();
649
            if ($itIsRootAcl) {
650
                $mask = $extension->adaptRootMask($mask, $privilege->getIdentity()->getId());
651
            }
652
            if ($extension->removeServiceBits($mask) === 0) {
653
                $supportedPermissions = array_intersect($permissions, $extension->getPermissions($mask));
654
655
                foreach ($supportedPermissions as $permission) {
656
                    if (!$privilege->hasPermission($permission)) {
657
                        $privilege->addPermission(new AclPermission($permission, AccessLevel::NONE_LEVEL));
658
                    }
659
                }
660
            } else {
661
                foreach ($extension->getPermissions($mask) as $permission) {
662
                    if (!$privilege->hasPermission($permission) && in_array($permission, $permissions)) {
663
                        $privilege->addPermission($this->getAclPermission($extension, $permission, $mask, $privilege));
664
                    }
665
                }
666
            }
667
668
        }
669
    }
670
671
    /**
672
     * Return AclPermission object for given permission, ACL mask and ACL privilege
673
     *
674
     * @param AclExtensionInterface $extension
675
     * @param string                $permission
676
     * @param string                $mask
677
     * @param AclPrivilege          $privilege
678
     * @return AclPermission
679
     */
680
    protected function getAclPermission(AclExtensionInterface $extension, $permission, $mask, AclPrivilege $privilege)
681
    {
682
        return new AclPermission(
683
            $permission,
684
            $extension->getAccessLevel(
685
                $mask,
686
                $permission,
687
                $privilege->getIdentity()->getId()
688
            )
689
        );
690
    }
691
}
692