Completed
Push — master ( 926740...1237f3 )
by Daniel
91:04 queued 79:23
created

ApiNormalizer::isRestrictedResource()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 6
rs 10
ccs 0
cts 4
cp 0
cc 3
nc 2
nop 1
crap 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Silverback\ApiComponentBundle\Serializer;
6
7
use Silverback\ApiComponentBundle\DataTransformer\DataTransformerInterface;
8
use Silverback\ApiComponentBundle\Entity\Content\AbstractContent;
9
use Silverback\ApiComponentBundle\Entity\RestrictedResourceInterface;
10
use Symfony\Component\Security\Core\Security;
11
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
12
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
13
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
14
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
15
16
class ApiNormalizer implements ContextAwareNormalizerInterface, NormalizerAwareInterface, CacheableSupportsMethodInterface
17
{
18
    use NormalizerAwareTrait;
19
20
    private const ALREADY_CALLED = 'API_COMPONENT_BUNDLE_NORMALIZER_ALREADY_CALLED';
21
22
    /** @var iterable|DataTransformerInterface[] */
23
    private $dataTransformers;
24
25
    /** @var DataTransformerInterface[] */
26
    private $supportedTransformers = [];
27
28
    private $security;
29
30
    public function __construct(iterable $dataTransformers = [], Security $security)
31
    {
32
        $this->dataTransformers = $dataTransformers;
33
        $this->security = $security;
34
    }
35
36
    private function isRestrictedResource($data): ?RestrictedResourceInterface
37
    {
38
        if ($data instanceof RestrictedResourceInterface && !$data instanceof AbstractContent) {
39
            return $data;
40
        }
41
        return null;
42
    }
43
44
    public function supportsNormalization($data, $format = null, array $context = []): bool
45
    {
46
        if (!is_object($data) || isset($context[self::ALREADY_CALLED])) {
47
            return false;
48
        }
49
50
        if ($this->isRestrictedResource($data)) {
51
            return true;
52
        }
53
54
        $this->supportedTransformers = [];
55
        foreach ($this->dataTransformers as $transformer) {
56
            if ($transformer->supportsTransformation($data)) {
57
                $this->supportedTransformers[] = $transformer;
58
            }
59
        }
60
        return !empty($this->supportedTransformers);
61
    }
62
63
    private function rolesVote(iterable $roles): bool
64
    {
65
        $negativeRoles = [];
66
        $positiveRoles = [];
67
        foreach ($roles as $role) {
68
            if (strpos($role, '!') === 0) {
69
                $negativeRoles[] = substr($role, 1);
70
                continue;
71
            }
72
            $positiveRoles[] = $role;
73
        }
74
        $positivePass = count($positiveRoles) && $this->security->isGranted($positiveRoles);
75
        $negativePass = count($negativeRoles) && !$this->security->isGranted($negativeRoles);
76
        return $positivePass || $negativePass;
77
    }
78
79
    public function normalize($object, $format = null, array $context = [])
80
    {
81
        if (
82
            ($restrictedResource = $this->isRestrictedResource($object)) &&
83
            ($roles = $restrictedResource->getSecurityRoles()) !== null &&
84
            !$this->rolesVote($roles)
85
        ) {
86
            return null;
87
        }
88
89
        foreach ($this->supportedTransformers as $transformer) {
90
            $transformer->transform($object, $context);
91
        }
92
        $context[self::ALREADY_CALLED] = true;
93
94
        return $this->normalizer->normalize($object, $format, $context);
95
    }
96
97
    public function hasCacheableSupportsMethod(): bool
98
    {
99
        return false;
100
    }
101
}
102