Passed
Push — master ( 8c9dd9...52f75f )
by Mr
02:31
created

LaminasAclProvisioner::buildAssertion()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 0
loc 15
ccs 0
cts 13
cp 0
crap 12
rs 9.9666
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the daikon-cqrs/security-interop project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Daikon\Security\Authorization;
10
11
use Auryn\Injector;
12
use Daikon\Boot\Service\ServiceDefinitionInterface;
13
use Daikon\Boot\Service\Provisioner\ProvisionerInterface;
14
use Daikon\Config\ConfigProviderInterface;
15
use Laminas\Permissions\Acl\Acl;
16
use Laminas\Permissions\Acl\Assertion\AssertionInterface;
17
use Laminas\Permissions\Acl\Assertion\ExpressionAssertion;
18
19
final class LaminasAclProvisioner implements ProvisionerInterface
20
{
21
    public function provision(
22
        Injector $injector,
23
        ConfigProviderInterface $configProvider,
24
        ServiceDefinitionInterface $serviceDefinition
25
    ): void {
26
        $className = $serviceDefinition->getServiceClass();
27
        $settings = $serviceDefinition->getSettings();
28
        $roles = $settings['roles'] ?? [];
29
        $resources = $settings['resources'] ?? [];
30
31
        $injector
32
            ->alias(AuthorizationServiceInterface::class, $className)
33
            ->share($className)
34
            ->delegate(
35
                $className,
36
                function () use ($className, $roles, $resources): AuthorizationServiceInterface {
37
                    $acl = new Acl;
38
                    array_map([$acl, 'addRole'], array_keys($roles), $roles);
39
                    array_map([$acl, 'addResource'], array_keys($resources));
40
                    foreach ($resources as $resource => $rules) {
41
                        $allows = $rules['allow'] ?? [];
42
                        $denies = $rules['deny'] ?? [];
43
                        //@todo support registration order for rule types
44
                        $this->registerRules($acl, Acl::TYPE_ALLOW, $allows, $roles, $resource);
45
                        $this->registerRules($acl, Acl::TYPE_DENY, $denies, $roles, $resource);
46
                    }
47
                    /** @var AuthorizationServiceInterface */
48
                    return new $className($acl);
49
                }
50
            );
51
    }
52
53
    private function registerRules(Acl &$acl, string $type, array $rules, array $roles, string $resource = null): void
54
    {
55
        foreach ($rules as $rule) {
56
            $ruleRoles = (array)($rule['roles'] ?? array_keys($roles));
57
            $assertions = (array)($rule['asserts'] ?? [null]);
58
            $privileges = $rule['privileges'] ?? null;
59
            foreach ($this->buildAssertions($assertions) as $assertion) {
60
                $acl->setRule(Acl::OP_ADD, $type, $ruleRoles, $resource, $privileges, $assertion);
61
            }
62
        }
63
    }
64
65
    private function buildAssertions(array $asserts): array
66
    {
67
        $assertions = [];
68
        array_push($assertions, ...array_map([$this, 'buildAssertion'], $asserts));
69
        return $assertions;
70
    }
71
72
    /** @param mixed $assert */
73
    private function buildAssertion($assert = null): ?AssertionInterface
74
    {
75
        $assertion = null;
76
        if (is_string($assert)) {
77
            /** @var AssertionInterface $assertion */
78
            $assertion = new $assert;
79
        } elseif (is_array($assert)) {
80
            /** @var AssertionInterface $assertion */
81
            $assertion = ExpressionAssertion::fromProperties(
82
                [ExpressionAssertion::OPERAND_CONTEXT_PROPERTY => $assert[0]],
83
                $assert[1],
84
                $this->resolveOperandValue($assert[2])
85
            );
86
        }
87
        return $assertion;
88
    }
89
90
    /**
91
     * @param mixed|array $operand
92
     * @return mixed
93
     */
94
    private function resolveOperandValue($operand)
95
    {
96
        if (is_callable($operand) === true) {
97
            return $operand();
98
        }
99
        return $operand;
100
    }
101
}
102