Completed
Push — master ( f9c626...d2dfab )
by Neomerx
09:40 queued 10s
created

BaseRuleAlgorithm::evaluateIsPermit()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 2
crap 2
1
<?php namespace Limoncello\Auth\Authorization\PolicyDecision\Algorithms;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Limoncello\Auth\Contracts\Authorization\PolicyAdministration\EvaluationEnum;
20
use Limoncello\Auth\Contracts\Authorization\PolicyAdministration\RuleCombiningAlgorithmInterface;
21
use Limoncello\Auth\Contracts\Authorization\PolicyAdministration\RuleInterface;
22
use Limoncello\Auth\Contracts\Authorization\PolicyAdministration\TargetMatchEnum;
23
use Limoncello\Auth\Contracts\Authorization\PolicyInformation\ContextInterface;
24
use Psr\Log\LoggerInterface;
25
use RuntimeException;
26
27
/**
28
 * @package Limoncello\Auth
29
 */
30
abstract class BaseRuleAlgorithm extends BaseAlgorithm implements RuleCombiningAlgorithmInterface
31
{
32
    /**
33
     * @param array $targets
34
     *
35
     * @return array
36
     */
37
    abstract protected function optimizeTargets(array $targets): array;
38
39
    /**
40
     * @param ContextInterface     $context
41
     * @param array                $rulesData
42
     * @param LoggerInterface|null $logger
43
     *
44
     * @return array
45
     *
46
     * @SuppressWarnings(PHPMD.StaticAccess)
47
     */
48 21
    public static function callRuleAlgorithm(
49
        ContextInterface $context,
50
        array $rulesData,
51
        LoggerInterface $logger = null
52
    ): array {
53 21
        return static::callAlgorithm(
54 21
            static::getCallable($rulesData),
0 ignored issues
show
Bug introduced by
Since getCallable() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getCallable() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
55 21
            $context,
56 21
            static::getTargets($rulesData),
0 ignored issues
show
Bug introduced by
Since getTargets() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getTargets() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
57 21
            static::getRules($rulesData),
0 ignored issues
show
Bug introduced by
Since getRules() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getRules() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
58 21
            $logger
59
        );
60
    }
61
62
    /**
63
     * @inheritdoc
64
     *
65
     * @SuppressWarnings(PHPMD.StaticAccess)
66
     */
67 36
    public function optimize(array $rules): array
68
    {
69 36
        assert(empty($rules) === false);
70
71 36
        $ruleId       = 0;
72 36
        $rawTargets   = [];
73 36
        $encodedRules = [];
74 36
        foreach ($rules as $rule) {
75
            /** @var RuleInterface $rule */
76 36
            $rawTargets[$ruleId]   = $rule->getTarget();
77 36
            $encodedRules[$ruleId] = Encoder::encodeRule($rule);
78 36
            $ruleId++;
79
        }
80
81 36
        $callable         = static::METHOD;
82 36
        $optimizedTargets = $this->optimizeTargets($rawTargets);
83
84
        /** @var callable|array $callable */
85 36
        assert($callable !== null && is_array($callable) === true &&
86 36
            is_callable($callable) === true && count($callable) === 2 &&
87 36
            is_string($callable[0]) === true && is_string($callable[1]) === true);
88
89
        return [
90 36
            static::INDEX_TARGETS  => $optimizedTargets,
91 36
            static::INDEX_RULES    => $encodedRules,
92 36
            static::INDEX_CALLABLE => $callable,
93
        ];
94
    }
95
96
    /**
97
     * @inheritdoc
98
     *
99
     * @SuppressWarnings(PHPMD.StaticAccess)
100
     */
101 24
    public static function evaluateItem(
102
        ContextInterface $context,
103
        int $match,
104
        array $encodedItem,
105
        ?LoggerInterface $logger
106
    ): array {
107 24
        return static::evaluateRule($context, $match, $encodedItem, $logger);
0 ignored issues
show
Bug introduced by
Since evaluateRule() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of evaluateRule() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
108
    }
109
110
    /**
111
     * @param ContextInterface     $context
112
     * @param int                  $match
113
     * @param array                $encodedRule
114
     * @param LoggerInterface|null $logger
115
     *
116
     * @return array
117
     *
118
     * @SuppressWarnings(PHPMD.StaticAccess)
119
     * @SuppressWarnings(PHPMD.ElseExpression)
120
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
121
     */
122 24
    private static function evaluateRule(
123
        ContextInterface $context,
124
        int $match,
125
        array $encodedRule,
126
        LoggerInterface $logger = null
127
    ): array {
128 24
        assert(Encoder::isRule($encodedRule));
129
130 24
        $ruleName = null;
131 24
        if ($logger !== null) {
132 7
            $ruleName  = Encoder::ruleName($encodedRule);
133 7
            $matchName = TargetMatchEnum::toString($match);
134 7
            $logger->debug("Rule '$ruleName' evaluation started for match '$matchName'.");
135
        }
136
137
        /** @see http://docs.oasis-open.org/xacml/3.0/xacml-3.0-core-spec-os-en.html #7.11 (table 4) */
138
139 24
        if ($match === TargetMatchEnum::INDETERMINATE) {
140 1
            $isPermit   = static::evaluateIsPermit($context, $encodedRule);
0 ignored issues
show
Bug introduced by
Since evaluateIsPermit() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of evaluateIsPermit() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
141 1
            $evaluation = $isPermit === true ?
142 1
                EvaluationEnum::INDETERMINATE_PERMIT : EvaluationEnum::INDETERMINATE_DENY;
143 24
        } elseif ($match === TargetMatchEnum::NO_TARGET || $match === TargetMatchEnum::MATCH) {
144 20
            $isPermit = static::evaluateIsPermit($context, $encodedRule);
0 ignored issues
show
Bug introduced by
Since evaluateIsPermit() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of evaluateIsPermit() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
145
            try {
146 20
                $condition = Encoder::ruleCondition($encodedRule);
147 20
                if (static::evaluateLogical($context, $condition) === false) {
0 ignored issues
show
Bug introduced by
It seems like $condition defined by \Limoncello\Auth\Authori...Condition($encodedRule) on line 146 can also be of type callable; however, Limoncello\Auth\Authoriz...ithm::evaluateLogical() does only seem to accept null|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
148 2
                    $evaluation = EvaluationEnum::NOT_APPLICABLE;
149
                } else {
150 17
                    $evaluation = $isPermit === true ? EvaluationEnum::PERMIT : EvaluationEnum::DENY;
151
                }
152 3
            } catch (RuntimeException $exception) {
153 3
                $evaluation = $isPermit === true ?
154 20
                    EvaluationEnum::INDETERMINATE_PERMIT : EvaluationEnum::INDETERMINATE_DENY;
155
            }
156
        } else {
157 12
            assert($match === TargetMatchEnum::NOT_MATCH);
158 12
            $evaluation = EvaluationEnum::NOT_APPLICABLE;
159
        }
160
161 24
        if ($logger !== null) {
162 7
            $evaluationName = EvaluationEnum::toString($evaluation);
163 7
            $logger->info("Rule '$ruleName' evaluated as '$evaluationName'.");
164
        }
165
166 24
        return static::packEvaluationResult(
167 24
            $evaluation,
168 24
            Encoder::getFulfillObligations($evaluation, Encoder::ruleObligations($encodedRule)),
169 24
            Encoder::getAppliedAdvice($evaluation, Encoder::ruleAdvice($encodedRule))
170
        );
171
    }
172
173
    /**
174
     * @param ContextInterface $context
175
     * @param array            $encodedRule
176
     *
177
     * @return bool
178
     */
179 21
    private static function evaluateIsPermit(ContextInterface $context, array $encodedRule): bool
180
    {
181 21
        $ruleEffect = Encoder::ruleEffect($encodedRule);
182
183
        try {
184 21
            $isPermit = static::evaluateLogical($context, $ruleEffect);
185 1
        } catch (RuntimeException $exception) {
186 1
            $isPermit = false;
187
        }
188
189 21
        return $isPermit;
190
    }
191
192
    /**
193
     * @param array $rulesData
194
     *
195
     * @return array
196
     */
197 21
    private static function getTargets(array $rulesData): array
198
    {
199 21
        return $rulesData[self::INDEX_TARGETS];
200
    }
201
202
    /**
203
     * @param array $rulesData
204
     *
205
     * @return array
206
     */
207 21
    private static function getRules(array $rulesData): array
208
    {
209 21
        return $rulesData[self::INDEX_RULES];
210
    }
211
212
    /**
213
     * @param array $rulesData
214
     *
215
     * @return callable
216
     */
217 21
    private static function getCallable(array $rulesData)
218
    {
219 21
        return $rulesData[self::INDEX_CALLABLE];
220
    }
221
}
222