EntityAclExtension   F
last analyzed

Complexity

Total Complexity 122

Size/Duplication

Total Lines 819
Duplicated Lines 4.76 %

Coupling/Cohesion

Components 1
Dependencies 22
Metric Value
wmc 122
lcom 1
cbo 22
dl 39
loc 819
rs 1.263

37 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 17 17 1
A getMasks() 0 6 1
A hasMasks() 0 6 1
A setEntityOwnerAccessor() 0 4 1
A getAccessLevelNames() 0 15 2
A getExtensionKey() 0 4 1
C validateMask() 0 27 7
A getObjectIdentity() 0 13 4
A getMaskBuilder() 0 10 2
A getAllMaskBuilders() 0 9 2
A getMaskPattern() 0 7 1
C adaptRootMask() 10 35 12
A getServiceBits() 0 4 1
A removeServiceBits() 0 4 1
B getAccessLevel() 4 23 5
D getPermissions() 5 30 9
A getClasses() 0 4 1
C decideIsGranting() 0 67 14
A fixMaxAccessLevel() 0 4 1
A fromDescriptor() 0 15 3
A fromDomainObject() 0 15 3
C getValidMasks() 0 50 9
A getMetadata() 0 4 1
A getObjectClassName() 0 13 3
A getMaskBuilderConst() 0 9 1
A isAccessDeniedByOrganizationContext() 0 18 4
B loadPermissions() 0 21 5
A buildPermissionsMap() 0 20 4
A getPermissionsToIdentityMap() 0 13 2
A getIdentityForPrimaryKey() 0 6 1
A getIdentityForPermission() 0 6 1
A getPermissionNumber() 0 7 1
A getPermissionsForIdentity() 0 8 2
A getAllowedPermissions() 0 21 4
A getPermissionsForType() 0 17 2
B validateMaskAccessLevel() 0 26 5
A supports() 3 19 4

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like EntityAclExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EntityAclExtension, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Oro\Bundle\SecurityBundle\Acl\Extension;
4
5
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
6
use Symfony\Component\Security\Acl\Exception\InvalidDomainObjectException;
7
use Symfony\Component\Security\Acl\Model\ObjectIdentityInterface;
8
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
9
use Symfony\Component\Security\Core\Util\ClassUtils;
10
11
use Oro\Bundle\EntityBundle\Exception\InvalidEntityException;
12
use Oro\Bundle\EntityBundle\ORM\EntityClassResolver;
13
use Oro\Bundle\SecurityBundle\Acl\AccessLevel;
14
use Oro\Bundle\SecurityBundle\Acl\Domain\ObjectIdAccessor;
15
use Oro\Bundle\SecurityBundle\Acl\Domain\ObjectIdentityFactory;
16
use Oro\Bundle\SecurityBundle\Acl\Exception\InvalidAclMaskException;
17
use Oro\Bundle\SecurityBundle\Acl\Group\AclGroupProviderInterface;
18
use Oro\Bundle\SecurityBundle\Acl\Permission\PermissionManager;
19
use Oro\Bundle\SecurityBundle\Annotation\Acl as AclAnnotation;
20
use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationContextTokenInterface;
21
use Oro\Bundle\SecurityBundle\Entity\Permission;
22
use Oro\Bundle\SecurityBundle\Metadata\EntitySecurityMetadataProvider;
23
use Oro\Bundle\SecurityBundle\Owner\EntityOwnerAccessor;
24
use Oro\Bundle\SecurityBundle\Owner\Metadata\MetadataProviderInterface;
25
use Oro\Bundle\SecurityBundle\Owner\Metadata\OwnershipMetadataInterface;
26
27
/**
28
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
29
 * @SuppressWarnings(PHPMD.TooManyMethods)
30
 */
31
class EntityAclExtension extends AbstractAclExtension
32
{
33
    const NAME = 'entity';
34
35
    /** @var ObjectIdAccessor */
36
    protected $objectIdAccessor;
37
38
    /** @var EntityClassResolver */
39
    protected $entityClassResolver;
40
41
    /** @var MetadataProviderInterface */
42
    protected $metadataProvider;
43
44
    /** @var EntitySecurityMetadataProvider */
45
    protected $entityMetadataProvider;
46
47
    /** @var AccessLevelOwnershipDecisionMakerInterface */
48
    protected $decisionMaker;
49
50
    /** @var EntityOwnerAccessor */
51
    protected $entityOwnerAccessor;
52
53
    /** @var PermissionManager */
54
    protected $permissionManager;
55
56
    /** @var AclGroupProviderInterface */
57
    protected $groupProvider;
58
59
    /**
60
     * key = Permission
61
     * value = The identity of a permission mask builder
62
     *
63
     * @var int[]
64
     */
65
    protected $permissionToMaskBuilderIdentity;
66
67
    /** @var array */
68
    protected $maskBuilderIdentityToPermissions;
69
70
    /**
71
     * @param ObjectIdAccessor $objectIdAccessor
72
     * @param EntityClassResolver $entityClassResolver
73
     * @param EntitySecurityMetadataProvider $entityMetadataProvider
74
     * @param MetadataProviderInterface $metadataProvider
75
     * @param AccessLevelOwnershipDecisionMakerInterface $decisionMaker
76
     * @param PermissionManager $permissionManager
77
     * @param AclGroupProviderInterface $groupProvider
78
     */
79 View Code Duplication
    public function __construct(
80
        ObjectIdAccessor $objectIdAccessor,
81
        EntityClassResolver $entityClassResolver,
82
        EntitySecurityMetadataProvider $entityMetadataProvider,
83
        MetadataProviderInterface $metadataProvider,
84
        AccessLevelOwnershipDecisionMakerInterface $decisionMaker,
85
        PermissionManager $permissionManager,
86
        AclGroupProviderInterface $groupProvider
87
    ) {
88
        $this->objectIdAccessor       = $objectIdAccessor;
89
        $this->entityClassResolver    = $entityClassResolver;
90
        $this->entityMetadataProvider = $entityMetadataProvider;
91
        $this->metadataProvider       = $metadataProvider;
92
        $this->decisionMaker          = $decisionMaker;
93
        $this->permissionManager      = $permissionManager;
94
        $this->groupProvider          = $groupProvider;
95
    }
96
97
    /**
98
     * {@inheritdoc}
99
     */
100
    public function getMasks($permission)
101
    {
102
        $this->buildPermissionsMap();
103
104
        return parent::getMasks($permission);
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function hasMasks($permission)
111
    {
112
        $this->buildPermissionsMap();
113
114
        return parent::hasMasks($permission);
115
    }
116
117
    /**
118
     * @param EntityOwnerAccessor $entityOwnerAccessor
119
     */
120
    public function setEntityOwnerAccessor(EntityOwnerAccessor $entityOwnerAccessor)
121
    {
122
        $this->entityOwnerAccessor = $entityOwnerAccessor;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function getAccessLevelNames($object)
129
    {
130
        if ($this->getObjectClassName($object) === ObjectIdentityFactory::ROOT_IDENTITY_TYPE) {
131
            /**
132
             * In community version root entity should not have GLOBAL(Organization) access level
133
             */
134
            return AccessLevel::getAccessLevelNames(
135
                AccessLevel::BASIC_LEVEL,
136
                AccessLevel::SYSTEM_LEVEL,
137
                [AccessLevel::GLOBAL_LEVEL]
138
            );
139
        } else {
140
            return $this->getMetadata($object)->getAccessLevelNames();
141
        }
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     */
147
    public function supports($type, $id)
148
    {
149
        if ($type === ObjectIdentityFactory::ROOT_IDENTITY_TYPE) {
150
            return $id === $this->getExtensionKey();
151
        }
152
153
        $delim = strpos($type, '@');
154 View Code Duplication
        if ($delim !== false) {
155
            $type = ltrim(substr($type, $delim + 1), ' ');
156
        }
157
158
        if ($id === $this->getExtensionKey()) {
159
            $type = $this->entityClassResolver->getEntityClass(ClassUtils::getRealClass($type));
160
        } else {
161
            $type = ClassUtils::getRealClass($type);
162
        }
163
164
        return $this->entityClassResolver->isEntity($type);
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170
    public function getExtensionKey()
171
    {
172
        return self::NAME;
173
    }
174
175
    /**
176
     * {@inheritdoc}
177
     */
178
    public function validateMask($mask, $object, $permission = null)
179
    {
180
        if (0 === $this->removeServiceBits($mask)) {
181
            // zero mask
182
            return;
183
        }
184
185
        $permissions = $permission === null
186
            ? $this->getPermissions($mask, true)
187
            : [$permission];
188
189
        foreach ($permissions as $permission) {
190
            $validMasks = $this->getValidMasks($permission, $object);
191
            if (($mask | $validMasks) === $validMasks) {
192
                $identity = $this->getIdentityForPermission($permission);
193
                foreach ($this->getPermissionsToIdentityMap() as $p => $i) {
194
                    if ($identity === $i) {
195
                        $this->validateMaskAccessLevel($p, $mask, $object);
196
                    }
197
                }
198
199
                return;
200
            }
201
        }
202
203
        throw $this->createInvalidAclMaskException($mask, $object);
204
    }
205
206
    /**
207
     * {@inheritdoc}
208
     */
209
    public function getObjectIdentity($val)
210
    {
211
        if (is_string($val)) {
212
            return $this->fromDescriptor($val);
213
        } elseif ($val instanceof AclAnnotation) {
214
            $class = $this->entityClassResolver->getEntityClass($val->getClass());
215
            $group = $val->getGroup();
216
217
            return new ObjectIdentity($val->getType(), !empty($group) ? $group . '@' . $class : $class);
218
        }
219
220
        return $this->fromDomainObject($val);
221
    }
222
223
    /**
224
     * {@inheritdoc}
225
     */
226
    public function getMaskBuilder($permission)
227
    {
228
        if (empty($permission)) {
229
            $permission = 'VIEW';
230
        }
231
232
        $identity = $this->getIdentityForPermission($permission);
233
234
        return new EntityMaskBuilder($identity, $this->getPermissionsForIdentity($identity));
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240
    public function getAllMaskBuilders()
241
    {
242
        $result = [];
243
        foreach ($this->getPermissionsForIdentity() as $identity => $permissions) {
244
            $result[] = new EntityMaskBuilder($identity, $permissions);
245
        }
246
247
        return $result;
248
    }
249
250
    /**
251
     * {@inheritdoc}
252
     */
253
    public function getMaskPattern($mask)
254
    {
255
        $identity    = $this->getServiceBits($mask);
256
        $maskBuilder = new EntityMaskBuilder($identity, $this->getPermissionsForIdentity($identity));
257
258
        return $maskBuilder->getPatternFor($mask);
259
    }
260
261
    /**
262
     * {@inheritdoc}
263
     *
264
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
265
     */
266
    public function adaptRootMask($rootMask, $object)
267
    {
268
        $permissions = $this->getPermissions($rootMask, true);
269
        if (!empty($permissions)) {
270
            $metadata = $this->getMetadata($object);
271
            $identity = $this->getServiceBits($rootMask);
272
            foreach ($permissions as $permission) {
273
                $permissionMask = $this->getMaskBuilderConst($identity, 'GROUP_' . $permission);
274
                $mask           = $rootMask & $permissionMask;
275
                $accessLevel    = $this->getAccessLevel($mask);
276
                if (!$metadata->hasOwner()) {
277
                    if ($identity === $this->getIdentityForPermission('ASSIGN')
278
                        && ($permission === 'ASSIGN' || $permission === 'SHARE')
279
                    ) {
280
                        $rootMask &= ~$this->removeServiceBits($mask);
281 View Code Duplication
                    } elseif ($accessLevel < AccessLevel::SYSTEM_LEVEL) {
282
                        $rootMask &= ~$this->removeServiceBits($mask);
283
                        $rootMask |= $this->getMaskBuilderConst($identity, 'MASK_' . $permission . '_SYSTEM');
284
                    }
285 View Code Duplication
                } elseif ($metadata->isGlobalLevelOwned()) {
286
                    if ($accessLevel < AccessLevel::GLOBAL_LEVEL) {
287
                        $rootMask &= ~$this->removeServiceBits($mask);
288
                        $rootMask |= $this->getMaskBuilderConst($identity, 'MASK_' . $permission . '_GLOBAL');
289
                    }
290
                } elseif ($metadata->isLocalLevelOwned()) {
291
                    if ($accessLevel < AccessLevel::LOCAL_LEVEL) {
292
                        $rootMask &= ~$this->removeServiceBits($mask);
293
                        $rootMask |= $this->getMaskBuilderConst($identity, 'MASK_' . $permission . '_LOCAL');
294
                    }
295
                }
296
            }
297
        }
298
299
        return $rootMask;
300
    }
301
302
    /**
303
     * {@inheritdoc}
304
     */
305
    public function getServiceBits($mask)
306
    {
307
        return $mask & EntityMaskBuilder::SERVICE_BITS;
308
    }
309
310
    /**
311
     * {@inheritdoc}
312
     */
313
    public function removeServiceBits($mask)
314
    {
315
        return $mask & EntityMaskBuilder::REMOVE_SERVICE_BITS;
316
    }
317
318
    /**
319
     * {@inheritdoc}
320
     */
321
    public function getAccessLevel($mask, $permission = null, $object = null)
322
    {
323
        if (0 === $this->removeServiceBits($mask)) {
324
            return AccessLevel::NONE_LEVEL;
325
        }
326
327
        $identity = $this->getServiceBits($mask);
328 View Code Duplication
        if ($permission !== null) {
329
            $permissionMask = $this->getMaskBuilderConst($identity, 'GROUP_' . $permission);
330
            $mask           = $mask & $permissionMask;
331
        }
332
333
        $mask = $this->removeServiceBits($mask);
334
335
        $result = AccessLevel::NONE_LEVEL;
336
        foreach (AccessLevel::$allAccessLevelNames as $accessLevel) {
337
            if (0 !== ($mask & $this->getMaskBuilderConst($identity, 'GROUP_' . $accessLevel))) {
338
                $result = AccessLevel::getConst($accessLevel . '_LEVEL');
339
            }
340
        }
341
342
        return $this->metadataProvider->getMaxAccessLevel($result, $this->getObjectClassName($object));
343
    }
344
345
    /**
346
     * {@inheritdoc}
347
     */
348
    public function getPermissions($mask = null, $setOnly = false, $byCurrentGroup = false)
349
    {
350
        $map = $this->getPermissionsToIdentityMap($byCurrentGroup);
351
352
        if ($mask === null) {
353
            return array_keys($map);
354
        }
355
356
        $result = [];
357
        if (!$setOnly) {
358
            $identity = $this->getServiceBits($mask);
359
            foreach ($map as $permission => $id) {
360
                if ($id === $identity) {
361
                    $result[] = $permission;
362
                }
363
            }
364
        } elseif (0 !== $this->removeServiceBits($mask)) {
365
            $identity = $this->getServiceBits($mask);
366
            $mask = $this->removeServiceBits($mask);
367
            foreach ($map as $permission => $id) {
368 View Code Duplication
                if ($id === $identity) {
369
                    if (0 !== ($mask & $this->getMaskBuilderConst($identity, 'GROUP_' . $permission))) {
370
                        $result[] = $permission;
371
                    }
372
                }
373
            }
374
        }
375
376
        return $result;
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382
    public function getAllowedPermissions(ObjectIdentity $oid)
383
    {
384
        if ($oid->getType() === ObjectIdentityFactory::ROOT_IDENTITY_TYPE) {
385
            $result = array_keys($this->getPermissionsToIdentityMap());
386
        } else {
387
            $config = $this->entityMetadataProvider->getMetadata($oid->getType());
388
            $result = $config->getPermissions();
389
            if (empty($result)) {
390
                $result = array_keys($this->getPermissionsToIdentityMap());
391
            }
392
393
            $metadata = $this->getMetadata($oid);
394
            if (!$metadata->hasOwner()) {
395
                $result = array_diff($result, ['ASSIGN', 'SHARE']);
396
            }
397
        }
398
399
        $allowed = $this->getPermissionsForType($oid->getType());
400
401
        return array_values(array_intersect($result, $allowed));
402
    }
403
404
    /**
405
     * @param string $type
406
     * @return array
407
     */
408
    protected function getPermissionsForType($type)
409
    {
410
        $group = $this->groupProvider->getGroup();
411
412
        if ($type === ObjectIdentityFactory::ROOT_IDENTITY_TYPE) {
413
            $permissions = $this->permissionManager->getPermissionsForGroup($group);
414
        } else {
415
            $permissions = $this->permissionManager->getPermissionsForEntity($type, $group);
416
        }
417
418
        return array_map(
419
            function (Permission $permission) {
420
                return $permission->getName();
421
            },
422
            $permissions
423
        );
424
    }
425
426
    /**
427
     * {@inheritdoc}
428
     */
429
    public function getClasses()
430
    {
431
        return $this->entityMetadataProvider->getEntities();
432
    }
433
434
    /**
435
     * @SuppressWarnings(PHPMD.NPathComplexity)
436
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
437
     * {@inheritdoc}
438
     */
439
    public function decideIsGranting($triggeredMask, $object, TokenInterface $securityToken)
440
    {
441
        // check whether we check permissions for a domain object
442
        if ($object === null || !is_object($object) || $object instanceof ObjectIdentityInterface) {
443
            return true;
444
        }
445
446
        $organization = null;
447
        if ($securityToken instanceof OrganizationContextTokenInterface) {
448
            if ($this->isAccessDeniedByOrganizationContext($object, $securityToken)) {
449
                return false;
450
            }
451
            $organization = $securityToken->getOrganizationContext();
452
        }
453
454
        $accessLevel = $this->getAccessLevel($triggeredMask);
455
        if (AccessLevel::SYSTEM_LEVEL === $accessLevel) {
456
            return true;
457
        }
458
459
        $metadata = $this->getMetadata($object);
460
        if (!$metadata->hasOwner()) {
461
            return true;
462
        }
463
464
        $result = false;
465
        if (AccessLevel::BASIC_LEVEL === $accessLevel) {
466
            $result = $this->decisionMaker->isAssociatedWithBasicLevelEntity(
467
                $securityToken->getUser(),
468
                $object,
469
                $organization
470
            );
471
        } else {
472
            if ($metadata->isBasicLevelOwned()) {
473
                $result = $this->decisionMaker->isAssociatedWithBasicLevelEntity(
474
                    $securityToken->getUser(),
475
                    $object,
476
                    $organization
477
                );
478
            }
479
            if (!$result) {
480
                if (AccessLevel::LOCAL_LEVEL === $accessLevel) {
481
                    $result = $this->decisionMaker->isAssociatedWithLocalLevelEntity(
482
                        $securityToken->getUser(),
483
                        $object,
484
                        false,
485
                        $organization
486
                    );
487
                } elseif (AccessLevel::DEEP_LEVEL === $accessLevel) {
488
                    $result = $this->decisionMaker->isAssociatedWithLocalLevelEntity(
489
                        $securityToken->getUser(),
490
                        $object,
491
                        true,
492
                        $organization
493
                    );
494
                } elseif (AccessLevel::GLOBAL_LEVEL === $accessLevel) {
495
                    $result = $this->decisionMaker->isAssociatedWithGlobalLevelEntity(
496
                        $securityToken->getUser(),
497
                        $object,
498
                        $organization
499
                    );
500
                }
501
            }
502
        }
503
504
        return $result;
505
    }
506
507
    /**
508
     * @param int   $accessLevel Current object access level
509
     * @param mixed $object      Object for test
510
     *
511
     * @return int
512
     *
513
     * @deprecated since 1.8, use MetadataProviderInterface::getMaxAccessLevel instead
514
     */
515
    protected function fixMaxAccessLevel($accessLevel, $object)
516
    {
517
        return $this->metadataProvider->getMaxAccessLevel($accessLevel, $this->getObjectClassName($object));
518
    }
519
520
    /**
521
     * Constructs an ObjectIdentity for the given domain object
522
     *
523
     * @param string $descriptor
524
     *
525
     * @return ObjectIdentity
526
     * @throws \InvalidArgumentException
527
     */
528
    protected function fromDescriptor($descriptor)
529
    {
530
        $type = $id = $group = null;
531
        $this->parseDescriptor($descriptor, $type, $id, $group);
532
533
        $type = $this->entityClassResolver->getEntityClass(ClassUtils::getRealClass($type));
534
535
        if ($id === $this->getExtensionKey()) {
536
            return new ObjectIdentity($id, !empty($group) ? $group . '@' . $type : $type);
537
        }
538
539
        throw new \InvalidArgumentException(
540
            sprintf('Unsupported object identity descriptor: %s.', $descriptor)
541
        );
542
    }
543
544
    /**
545
     * Constructs an ObjectIdentity for the given domain object
546
     *
547
     * @param object $domainObject
548
     *
549
     * @return ObjectIdentity
550
     * @throws InvalidDomainObjectException
551
     */
552
    protected function fromDomainObject($domainObject)
553
    {
554
        if (!is_object($domainObject)) {
555
            throw new InvalidDomainObjectException('$domainObject must be an object.');
556
        }
557
558
        try {
559
            return new ObjectIdentity(
560
                $this->objectIdAccessor->getId($domainObject),
561
                ClassUtils::getRealClass($domainObject)
562
            );
563
        } catch (\InvalidArgumentException $invalid) {
564
            throw new InvalidDomainObjectException($invalid->getMessage(), 0, $invalid);
565
        }
566
    }
567
568
    /**
569
     * Checks that the given mask represents only one access level
570
     *
571
     * @param string $permission
572
     * @param int    $mask
573
     * @param mixed  $object
574
     *
575
     * @throws InvalidAclMaskException
576
     */
577
    protected function validateMaskAccessLevel($permission, $mask, $object)
578
    {
579
        $identity = $this->getIdentityForPermission($permission);
580
        if (0 !== ($mask & $this->getMaskBuilderConst($identity, 'GROUP_' . $permission))) {
581
            $maskAccessLevels = [];
582
            $clearedMask = $this->removeServiceBits($mask);
583
584
            foreach (AccessLevel::$allAccessLevelNames as $accessLevel) {
585
                $levelMask = $this->removeServiceBits(
586
                    $this->getMaskBuilderConst($identity, sprintf('MASK_%s_%s', $permission, $accessLevel))
587
                );
588
589
                if (0 !== ($clearedMask & $levelMask)) {
590
                    $maskAccessLevels[] = $accessLevel;
591
                }
592
            }
593
            if (count($maskAccessLevels) > 1) {
594
                $msg = sprintf(
595
                    'The %s mask must be in one access level only, but it is in %s access levels.',
596
                    $permission,
597
                    implode(', ', $maskAccessLevels)
598
                );
599
                throw $this->createInvalidAclMaskException($mask, $object, $msg);
600
            }
601
        }
602
    }
603
604
    /**
605
     * Gets all valid bitmasks for the given object
606
     *
607
     * @param string $permission
608
     * @param mixed  $object
609
     *
610
     * @return int
611
     */
612
    protected function getValidMasks($permission, $object)
613
    {
614
        $identity = $this->getIdentityForPermission($permission);
615
616
        if ($object instanceof ObjectIdentity && $object->getType() === ObjectIdentityFactory::ROOT_IDENTITY_TYPE) {
617
            return
618
                $this->getMaskBuilderConst($identity, 'GROUP_SYSTEM')
619
                | $this->getMaskBuilderConst($identity, 'GROUP_GLOBAL')
620
                | $this->getMaskBuilderConst($identity, 'GROUP_DEEP')
621
                | $this->getMaskBuilderConst($identity, 'GROUP_LOCAL')
622
                | $this->getMaskBuilderConst($identity, 'GROUP_BASIC');
623
        }
624
625
        $metadata = $this->getMetadata($object);
626
        if (!$metadata->hasOwner()) {
627
            $maskBuilder = $this->getMaskBuilder($permission);
628
            $maskBuilder->reset()->add($maskBuilder->getMask('GROUP_SYSTEM'));
629
630
            if ($maskBuilder->hasMask('MASK_ASSIGN_SYSTEM')) {
631
                $maskBuilder->remove('ASSIGN_SYSTEM');
632
            }
633
634
            if ($maskBuilder->hasMask('MASK_SHARE_SYSTEM')) {
635
                $maskBuilder->remove('SHARE_SYSTEM');
636
            }
637
638
            return $maskBuilder->get();
639
        }
640
641
        if ($metadata->isGlobalLevelOwned()) {
642
            return
643
                $this->getMaskBuilderConst($identity, 'GROUP_SYSTEM')
644
                | $this->getMaskBuilderConst($identity, 'GROUP_GLOBAL');
645
        } elseif ($metadata->isLocalLevelOwned()) {
646
            return
647
                $this->getMaskBuilderConst($identity, 'GROUP_SYSTEM')
648
                | $this->getMaskBuilderConst($identity, 'GROUP_GLOBAL')
649
                | $this->getMaskBuilderConst($identity, 'GROUP_DEEP')
650
                | $this->getMaskBuilderConst($identity, 'GROUP_LOCAL');
651
        } elseif ($metadata->isBasicLevelOwned()) {
652
            return
653
                $this->getMaskBuilderConst($identity, 'GROUP_SYSTEM')
654
                | $this->getMaskBuilderConst($identity, 'GROUP_GLOBAL')
655
                | $this->getMaskBuilderConst($identity, 'GROUP_DEEP')
656
                | $this->getMaskBuilderConst($identity, 'GROUP_LOCAL')
657
                | $this->getMaskBuilderConst($identity, 'GROUP_BASIC');
658
        }
659
660
        return $this->getIdentityForPermission($permission);
661
    }
662
663
    /**
664
     * Gets metadata for the given object
665
     *
666
     * @param mixed $object
667
     *
668
     * @return OwnershipMetadataInterface
669
     */
670
    protected function getMetadata($object)
671
    {
672
        return $this->metadataProvider->getMetadata($this->getObjectClassName($object));
673
    }
674
675
    /**
676
     * Gets class name for given object
677
     *
678
     * @param $object
679
     *
680
     * @return string
681
     */
682
    protected function getObjectClassName($object)
683
    {
684
        if ($object instanceof ObjectIdentity) {
685
            $className = $object->getType();
686
        } elseif (is_string($object)) {
687
            $className = $id = $group = null;
688
            $this->parseDescriptor($object, $className, $id, $group);
689
        } else {
690
            $className = ClassUtils::getRealClass($object);
691
        }
692
693
        return $className;
694
    }
695
696
    /**
697
     * Gets the constant value defined in the given permission mask builder
698
     *
699
     * @param int    $maskBuilderIdentity The permission mask builder identity
700
     * @param string $constName
701
     *
702
     * @return int
703
     */
704
    protected function getMaskBuilderConst($maskBuilderIdentity, $constName)
705
    {
706
        $maskBuilder = new EntityMaskBuilder(
707
            $maskBuilderIdentity,
708
            $this->getPermissionsForIdentity($maskBuilderIdentity)
709
        );
710
711
        return $maskBuilder->getMask($constName);
712
    }
713
714
    /**
715
     * Check organization. If user try to access entity what was created in organization this user do not have access -
716
     *  deny access. We should check organization for all the entities what have ownership
717
     *  (USER, BUSINESS_UNIT, ORGANIZATION ownership types)
718
     *
719
     * @param mixed $object
720
     * @param OrganizationContextTokenInterface $securityToken
721
     * @return bool
722
     */
723
    protected function isAccessDeniedByOrganizationContext($object, OrganizationContextTokenInterface $securityToken)
724
    {
725
        try {
726
            // try to get entity organization value
727
            $objectOrganization = $this->entityOwnerAccessor->getOrganization($object);
728
729
            // check entity organization with current organization
730
            if ($objectOrganization
731
                && $objectOrganization->getId() !== $securityToken->getOrganizationContext()->getId()
732
            ) {
733
                return true;
734
            }
735
        } catch (InvalidEntityException $e) {
736
            // in case if entity has no organization field (none ownership type)
737
        }
738
739
        return false;
740
    }
741
742
    protected function loadPermissions()
743
    {
744
        if (null !== $this->permissionToMaskBuilderIdentity && null !== $this->maskBuilderIdentityToPermissions) {
745
            return;
746
        }
747
748
        $allPermissions = $this->permissionManager->getPermissionsMap();
749
        $permissionChunks = array_chunk(array_keys($allPermissions), EntityMaskBuilder::MAX_PERMISSIONS_IN_MASK);
750
751
        foreach ($permissionChunks as $permissions) {
752
            foreach ($permissions as $permission) {
753
                $pk = $allPermissions[$permission];
754
755
                $identity = $this->getIdentityForPrimaryKey($pk);
756
                $number = $this->getPermissionNumber($pk);
757
758
                $this->permissionToMaskBuilderIdentity[$permission] = $identity;
759
                $this->maskBuilderIdentityToPermissions[$identity][$number] = $permission;
760
            }
761
        }
762
    }
763
764
    protected function buildPermissionsMap()
765
    {
766
        if ($this->map !== null) {
767
            return;
768
        }
769
770
        $this->map = [];
771
772
        $permissions = array_keys($this->getPermissionsToIdentityMap());
773
        foreach ($permissions as $permission) {
774
            $maskBuilder = $this->getMaskBuilder($permission);
775
            $masks = [];
776
777
            foreach (AccessLevel::$allAccessLevelNames as $accessLevel) {
778
                $masks[] = $maskBuilder->getMask(sprintf('MASK_%s_%s', $permission, $accessLevel));
779
            }
780
781
            $this->map[$permission] = $masks;
782
        }
783
    }
784
785
    /**
786
     * @param bool $byCurrentGroup
787
     * @return array|int[]
788
     */
789
    protected function getPermissionsToIdentityMap($byCurrentGroup = false)
790
    {
791
        $this->loadPermissions();
792
        $map = $this->permissionToMaskBuilderIdentity;
793
794
        if ($byCurrentGroup) {
795
            $permissions = $this->permissionManager->getPermissionsMap($this->groupProvider->getGroup());
796
797
            $map = array_intersect_key($map, $permissions);
798
        }
799
800
        return $map;
801
    }
802
803
    /**
804
     * @param int $pk
805
     * @return int
806
     */
807
    protected function getIdentityForPrimaryKey($pk)
808
    {
809
        $identity = (int) (($pk - 1) / EntityMaskBuilder::MAX_PERMISSIONS_IN_MASK);
810
811
        return $identity << (count(AccessLevel::$allAccessLevelNames) * EntityMaskBuilder::MAX_PERMISSIONS_IN_MASK);
812
    }
813
814
    /**
815
     * @param string $permission
816
     * @return int
817
     */
818
    protected function getIdentityForPermission($permission)
819
    {
820
        $identities = $this->getPermissionsToIdentityMap();
821
822
        return $identities[$permission];
823
    }
824
825
    /**
826
     * @param int $pk
827
     * @return int
828
     */
829
    protected function getPermissionNumber($pk)
830
    {
831
        $map = range(0, EntityMaskBuilder::MAX_PERMISSIONS_IN_MASK - 1);
832
        array_unshift($map, array_pop($map));
833
834
        return $map[$pk % EntityMaskBuilder::MAX_PERMISSIONS_IN_MASK];
835
    }
836
837
    /**
838
     * @param int|null $identity
839
     * @return array
840
     */
841
    protected function getPermissionsForIdentity($identity = null)
842
    {
843
        $this->loadPermissions();
844
845
        return $identity === null
846
            ? $this->maskBuilderIdentityToPermissions
847
            : $this->maskBuilderIdentityToPermissions[$identity];
848
    }
849
}
850