Completed
Push — master ( 9a380f...b40ee9 )
by Łukasz
25:43
created

getCriterionForLimitation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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