Passed
Push — 1.x ( 78763f...5db5d7 )
by Marko
02:13
created

AccessCompilerPass::process()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 4
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace KunicMarko\SonataAnnotationBundle\DependencyInjection\Compiler;
6
7
use Doctrine\Common\Annotations\Reader;
8
use KunicMarko\SonataAnnotationBundle\Annotation\Access;
9
use KunicMarko\SonataAnnotationBundle\Annotation\Admin;
10
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
11
use Symfony\Component\DependencyInjection\ContainerBuilder;
12
use Symfony\Component\DependencyInjection\Definition;
13
use Symfony\Component\Finder\Finder;
14
use Symfony\Component\Finder\SplFileInfo;
15
16
/**
17
 * @author Marko Kunic <[email protected]>
18
 */
19
final class AccessCompilerPass implements CompilerPassInterface
20
{
21
    private const ENTITY_ARGUMENT_IN_SERVICE_DEFINITION = 1;
22
23
    /**
24
     * @var Reader
25
     */
26
    private $annotationReader;
27
28
    public function process(ContainerBuilder $container): void
29
    {
30
        $this->annotationReader = $container->get('annotation_reader');
31
        $roles = $container->getParameter('security.role_hierarchy.roles');
32
33
        foreach ($container->findTaggedServiceIds('sonata.admin') as $id => $tag) {
34
            if (!($class = $this->getClass($container, $id))) {
35
                continue;
36
            }
37
38
            if ($permissions = $this->getRoles(new \ReflectionClass($class), $this->getRolePrefix($id))) {
39
                $roles = array_merge_recursive($roles, $permissions);
40
            }
41
        }
42
43
        $container->setParameter('security.role_hierarchy.roles', $roles);
44
    }
45
46
    private function getClass(ContainerBuilder $container, string $id): ?string
47
    {
48
        $definition = $container->getDefinition($id);
49
50
        //Entity can be a class or a parameter
51
        $class = $definition->getArgument(self::ENTITY_ARGUMENT_IN_SERVICE_DEFINITION);
52
53
        if ($class[0] !== '%') {
54
            return $class;
55
        }
56
57
        if ($container->hasParameter($class = trim($class, '%'))) {
58
            return $container->getParameter($class);
59
        }
60
61
        throw new \LogicException(sprintf(
62
           'Service "%s" has a parameter "%s" that is not found.',
63
            $id,
64
            $class
65
        ));
66
    }
67
68
    private function getRolePrefix(string $serviceId): string
69
    {
70
        return 'ROLE_' . str_replace('.', '_', strtoupper($serviceId)) . '_';
71
    }
72
73
    private function getRoles(\ReflectionClass $class, string $prefix): array
74
    {
75
        $roles = [];
76
77
        foreach ($this->annotationReader->getClassAnnotations($class) as $annotation) {
78
            if (!$annotation instanceof Access) {
79
                continue;
80
            }
81
82
            $roles[$annotation->getRole()] = array_map(
83
                function (string $permission) use ($prefix) {
84
                    return $prefix . strtoupper($permission);
85
                },
86
                $annotation->permissions
87
            );
88
        }
89
90
        return $roles;
91
    }
92
}
93