AuthorizationRulesLoader::getActions()   B
last analyzed

Complexity

Conditions 8
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 7
cts 7
cp 1
rs 8.4444
c 0
b 0
f 0
cc 8
nc 3
nop 1
crap 8
1
<?php declare(strict_types=1);
2
3
namespace Limoncello\Application\Authorization;
4
5
/**
6
 * Copyright 2015-2020 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Limoncello\Application\Contracts\Authorization\AuthorizationRulesInterface;
22
use Limoncello\Application\Contracts\Authorization\ResourceAuthorizationRulesInterface;
23
use Limoncello\Auth\Authorization\PolicyAdministration\AllOf;
24
use Limoncello\Auth\Authorization\PolicyAdministration\AnyOf;
25
use Limoncello\Auth\Authorization\PolicyAdministration\Logical;
26
use Limoncello\Auth\Authorization\PolicyAdministration\Policy;
27
use Limoncello\Auth\Authorization\PolicyAdministration\PolicySet;
28
use Limoncello\Auth\Authorization\PolicyAdministration\Rule;
29
use Limoncello\Auth\Authorization\PolicyAdministration\Target;
30
use Limoncello\Auth\Authorization\PolicyDecision\PolicyAlgorithm;
31
use Limoncello\Auth\Authorization\PolicyDecision\PolicyDecisionPoint;
32
use Limoncello\Auth\Authorization\PolicyDecision\RuleAlgorithm;
33
use Limoncello\Auth\Contracts\Authorization\PolicyAdministration\PolicyInterface;
34
use Limoncello\Auth\Contracts\Authorization\PolicyAdministration\RuleInterface;
35
use Limoncello\Auth\Contracts\Authorization\PolicyAdministration\TargetInterface;
36
use Limoncello\Auth\Contracts\Authorization\PolicyInformation\ContextInterface;
37
use Limoncello\Common\Reflection\ClassIsTrait;
38
use ReflectionClass;
39
use ReflectionException;
40
use function array_key_exists;
41
use function assert;
42
use function class_implements;
43
use function is_int;
44
use function is_string;
45
use function iterator_to_array;
46
47
/**
48
 * @package Limoncello\Application
49
 *
50
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
51
 */
52
class AuthorizationRulesLoader
53
{
54
    use ClassIsTrait;
55
56
    /**
57
     * @var array
58
     */
59 2
    private $rulesData;
60
61 2
    /**
62
     * @param string $path
63
     * @param string $name
64
     *
65
     * @throws ReflectionException
66
     */
67
    public function __construct(string $path, string $name)
68
    {
69
        $this->rulesData = $this->loadData($path, $name);
70
    }
71
72
    /**
73
     * @param string $path
74 2
     * @param string $name
75
     *
76 2
     * @return array
77 2
     *
78 2
     * @throws ReflectionException
79 2
     *
80 2
     * @SuppressWarnings(PHPMD.StaticAccess)
81
     */
82 2
    private function loadData(string $path, string $name): array
83 2
    {
84
        $policies = [];
85 2
        foreach ($this->getAuthorizationRulesClasses($path) as $class) {
86
            $methodNames   = $this->getActions($class);
87
            $resourcesType = $this->getResourcesType($class);
88
            $policies[]    = $this->createClassPolicy($name, $class, $methodNames, $resourcesType);
89
        }
90
        $policySet    = (new PolicySet($policies, PolicyAlgorithm::firstApplicable()))->setName($name);
91
        $policiesData = (new PolicyDecisionPoint($policySet))->getEncodePolicySet();
92
93
        return $policiesData;
94
    }
95 2
96
    /**
97
     * @param string $policiesPath
98 2
     *
99
     * @return array
100
     *
101
     * @throws ReflectionException
102
     */
103
    private function getAuthorizationRulesClasses(string $policiesPath): array
104
    {
105
        /** @noinspection PhpParamsInspection */
106
        return iterator_to_array($this->selectClasses($policiesPath, AuthorizationRulesInterface::class));
107 2
    }
108
109 2
    /**
110
     * @param string $policyClass
111 2
     *
112 2
     * @return string[]
113 2
     * @throws ReflectionException
114 2
     */
115 2
    private function getActions(string $policyClass): array
116 2
    {
117 2
        $reflectionClass = new ReflectionClass($policyClass);
118 2
119
        $actions = [];
120 2
        foreach ($reflectionClass->getMethods() as $reflectionMethod) {
121
            if ($reflectionMethod->isPublic() === true &&
122
                $reflectionMethod->isStatic() === true &&
123
                $reflectionMethod->hasReturnType() === true &&
124 2
                (string)$reflectionMethod->getReturnType() === 'bool' &&
125
                $reflectionMethod->getNumberOfParameters() === 1 &&
126
                $reflectionMethod->getParameters()[0]->getClass()->implementsInterface(ContextInterface::class) === true
127
            ) {
128
                $actions[] = $reflectionMethod->getName();
129
            }
130
        }
131
132 2
        return $actions;
133
    }
134 2
135 2
    /**
136
     * @param string $className
137 2
     *
138
     * @return string|null
139
     */
140 2
    private function getResourcesType(string $className): ?string
141
    {
142
        $resourceType = null;
143
        if (array_key_exists(ResourceAuthorizationRulesInterface::class, class_implements($className)) === true) {
144
            /** @var ResourceAuthorizationRulesInterface $className */
145
            $resourceType = $className::getResourcesType();
146
        }
147
148
        return $resourceType;
149
    }
150
151
    /**
152
     * @param string      $policiesName
153 2
     * @param string      $class
154
     * @param array       $methods
155
     * @param string|null $resourcesType
156
     *
157
     * @return PolicyInterface
158
     *
159 2
     * @SuppressWarnings(PHPMD.StaticAccess)
160 2
     */
161 2
    private function createClassPolicy(
162
        string $policiesName,
163
        string $class,
164 2
        array $methods,
165 2
        string $resourcesType = null
166 2
    ): PolicyInterface {
167
        $rules = [];
168 2
        foreach ($methods as $method) {
169
            $rules[] = $this->createMethodRule($class, $method);
170
        }
171
172
        $policy = (new Policy($rules, RuleAlgorithm::firstApplicable()))
173
            ->setName($policiesName . ' -> ResourceType' . "=`$resourcesType`")
174
            ->setTarget($this->target(RequestProperties::REQ_RESOURCE_TYPE, $resourcesType));
175
176
        return $policy;
177 2
    }
178
179 2
    /**
180 2
     * @param string $class
181 2
     * @param string $method
182 2
     *
183 2
     * @return RuleInterface
184
     */
185 2
    private function createMethodRule(string $class, string $method): RuleInterface
186
    {
187
        $rule = (new Rule())
188
            ->setName($method)
189
            ->setTarget($this->target(RequestProperties::REQ_ACTION, $method))
190
            ->setEffect(new Logical([$class, $method]))
191
            ->setName("$class::$method");
192
193
        return $rule;
194 2
    }
195
196 2
    /**
197 2
     * @param string|int      $key
198
     * @param string|int|null $value
199 2
     *
200 2
     * @return TargetInterface
201 2
     */
202
    private function target($key, $value): TargetInterface
203 2
    {
204
        assert(is_string($key) || is_int($key));
205
        assert($value === null || is_string($value) || is_int($value));
206
207
        $allOfs = [new AllOf([$key => $value])];
208
        $anyOff = new AnyOf($allOfs);
209 2
        $target = (new Target($anyOff))->setName("$key=`$value`");
210
211 2
        return $target;
212
    }
213
214
    /**
215
     * @return array
216
     */
217
    public function getRulesData(): array
218
    {
219
        return $this->rulesData;
220
    }
221
}
222