Completed
Push — ezp-31079-follow-up ( d3da52...93ebb2 )
by
unknown
12:55
created

PermissionCriterionResolver   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 131
rs 10
c 0
b 0
f 0
wmc 18
lcom 1
cbo 7

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A getCriterionForLimitation() 0 9 2
C getPermissionsCriterion() 0 79 15
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
declare(strict_types=1);
8
9
namespace eZ\Publish\Core\Repository\Permission;
10
11
use eZ\Publish\API\Repository\PermissionCriterionResolver as APIPermissionCriterionResolver;
12
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd;
13
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOr;
14
use eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface;
15
use eZ\Publish\API\Repository\Values\User\Limitation;
16
use eZ\Publish\API\Repository\PermissionResolver as PermissionResolverInterface;
17
use eZ\Publish\API\Repository\Values\User\UserReference;
18
use eZ\Publish\Core\Limitation\TargetOnlyLimitationType;
19
use RuntimeException;
20
21
/**
22
 * Implementation of Permissions Criterion Resolver.
23
 */
24
class PermissionCriterionResolver implements APIPermissionCriterionResolver
25
{
26
    /** @var \eZ\Publish\API\Repository\PermissionResolver */
27
    private $permissionResolver;
28
29
    /** @var \eZ\Publish\Core\Repository\Permission\LimitationService */
30
    private $limitationService;
31
32
    /**
33
     * Constructor.
34
     *
35
     * @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver
36
     * @param \eZ\Publish\Core\Repository\Permission\LimitationService $limitationService
37
     */
38
    public function __construct(
39
        PermissionResolverInterface $permissionResolver,
40
        LimitationService $limitationService
41
    ) {
42
        $this->permissionResolver = $permissionResolver;
43
        $this->limitationService = $limitationService;
44
    }
45
46
    /**
47
     * Get permission criteria if needed and return false if no access at all.
48
     *
49
     * @uses \eZ\Publish\API\Repository\PermissionResolver::getCurrentUserReference()
50
     * @uses \eZ\Publish\API\Repository\PermissionResolver::hasAccess()
51
     *
52
     * @param string $module
53
     * @param string $function
54
     * @param array $targets
55
     *
56
     * @return bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion
57
     */
58
    public function getPermissionsCriterion(string $module = 'content', string $function = 'read', ?array $targets = null)
59
    {
60
        $permissionSets = $this->permissionResolver->hasAccess($module, $function);
61
        if (is_bool($permissionSets)) {
62
            return $permissionSets;
63
        }
64
65
        if (empty($permissionSets)) {
66
            throw new RuntimeException("Received an empty array of limitations from hasAccess( '{$module}', '{$function}' )");
67
        }
68
69
        /*
70
         * RoleAssignment is a OR condition, so is policy, while limitations is a AND condition
71
         *
72
         * If RoleAssignment has limitation then policy OR conditions are wrapped in a AND condition with the
73
         * role limitation, otherwise it will be merged into RoleAssignment's OR condition.
74
         */
75
        $currentUserRef = $this->permissionResolver->getCurrentUserReference();
76
        $roleAssignmentOrCriteria = [];
77
        foreach ($permissionSets as $permissionSet) {
78
            // $permissionSet is a RoleAssignment, but in the form of role limitation & role policies hash
79
            $policyOrCriteria = [];
80
            /** @var \eZ\Publish\API\Repository\Values\User\Policy */
81
            foreach ($permissionSet['policies'] as $policy) {
82
                $limitations = $policy->getLimitations();
83
                if (empty($limitations)) {
84
                    // Given policy gives full access, optimize away all role policies (but not role limitation if any)
85
                    // This should be optimized on create/update of Roles, however we keep this here for bc with older data
86
                    $policyOrCriteria = [];
87
                    break;
88
                }
89
90
                $limitationsAndCriteria = [];
91
                foreach ($limitations as $limitation) {
92
                    $limitationsAndCriteria[] = $this->getCriterionForLimitation($limitation, $currentUserRef, $targets);
93
                }
94
95
                $policyOrCriteria[] = isset($limitationsAndCriteria[1]) ?
96
                    new LogicalAnd($limitationsAndCriteria) :
97
                    $limitationsAndCriteria[0];
98
            }
99
100
            /**
101
             * Apply role limitations if there is one.
102
             *
103
             * @var \eZ\Publish\API\Repository\Values\User\Limitation[]
104
             */
105
            if ($permissionSet['limitation'] instanceof Limitation) {
106
                // We need to match both the limitation AND *one* of the policies, aka; roleLimit AND policies(OR)
107
                if (!empty($policyOrCriteria)) {
108
                    $criterion = $this->getCriterionForLimitation($permissionSet['limitation'], $currentUserRef, $targets);
109
                    $roleAssignmentOrCriteria[] = new LogicalAnd(
110
                        [
111
                            $criterion,
112
                            isset($policyOrCriteria[1]) ? new LogicalOr($policyOrCriteria) : $policyOrCriteria[0],
113
                        ]
114
                    );
115
                } else {
116
                    $roleAssignmentOrCriteria[] = $this->getCriterionForLimitation(
117
                        $permissionSet['limitation'], $currentUserRef, $targets
118
                    );
119
                }
120
            } elseif (!empty($policyOrCriteria)) {
121
                // Otherwise merge $policyOrCriteria into $roleAssignmentOrCriteria
122
                // There is no role limitation, so any of the policies can globally match in the returned OR criteria
123
                $roleAssignmentOrCriteria = empty($roleAssignmentOrCriteria) ?
124
                    $policyOrCriteria :
125
                    array_merge($roleAssignmentOrCriteria, $policyOrCriteria);
126
            }
127
        }
128
129
        if (empty($roleAssignmentOrCriteria)) {
130
            return false;
131
        }
132
133
        return isset($roleAssignmentOrCriteria[1]) ?
134
            new LogicalOr($roleAssignmentOrCriteria) :
135
            $roleAssignmentOrCriteria[0];
136
    }
137
138
    /**
139
     * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation
140
     * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUserRef
141
     * @param array|null $targets
142
     *
143
     * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface|\eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOperator
144
     */
145
    private function getCriterionForLimitation(Limitation $limitation, UserReference $currentUserRef, ?array $targets): CriterionInterface
146
    {
147
        $type = $this->limitationService->getLimitationType($limitation->getIdentifier());
148
        if ($type instanceof TargetOnlyLimitationType) {
149
            return $type->getCriterionByTarget($limitation, $currentUserRef, $targets);
150
        }
151
152
        return $type->getCriterion($limitation, $currentUserRef);
153
    }
154
}
155