Completed
Push — master ( aa6f89...863f7f )
by John
08:07
created

RbacRequestVoter::vote()   C

Complexity

Conditions 8
Paths 14

Size

Total Lines 48
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 48
rs 5.9322
cc 8
eloc 25
nc 14
nop 3
1
<?php declare(strict_types = 1);
2
/*
3
 * This file is part of the KleijnWeb\SwaggerBundle package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
namespace KleijnWeb\SwaggerBundle\Security;
9
10
use KleijnWeb\PhpApi\Descriptions\Description\Repository;
11
use KleijnWeb\SwaggerBundle\EventListener\Request\RequestMeta;
12
use Symfony\Component\HttpFoundation\Request;
13
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
14
use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;
15
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
16
17
/**
18
 * @author John Kleijn <[email protected]>
19
 */
20
class RbacRequestVoter implements VoterInterface
21
{
22
    /**
23
     * @var Repository
24
     */
25
    private $documentRepository;
26
27
    /**
28
     * @var AccessDecisionManagerInterface
29
     */
30
    private $decisionManager;
31
32
    /**
33
     * @param Repository                     $documentRepository
34
     * @param AccessDecisionManagerInterface $decisionManager
35
     */
36
    public function __construct(Repository $documentRepository, AccessDecisionManagerInterface $decisionManager)
37
    {
38
        $this->documentRepository = $documentRepository;
39
        $this->decisionManager    = $decisionManager;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45
    public function vote(TokenInterface $token, $request, array $attributes)
46
    {
47
        $vote = VoterInterface::ACCESS_ABSTAIN;
48
49
        if (!$request instanceof Request) {
50
            return $vote;
51
        }
52
53
        // TODO Duplicated many times
54
        if (!$request->attributes->has(RequestMeta::ATTRIBUTE_URI)) {
55
            return $vote;
56
        }
57
58
        // TODO These next 2 statements are duplicated about 3 times now
59
        $description = $this->documentRepository
60
            ->get($request->attributes->get(RequestMeta::ATTRIBUTE_URI));
61
62
        $operation = $description
63
            ->getPath($request->attributes->get(RequestMeta::ATTRIBUTE_PATH))
64
            ->getOperation($request->getMethod());
65
66
67
        // If the operation is secured, IS_AUTHENTICATED_FULLY unless overridden by x-rbac
68
        if ($operation->isSecured()) {
69
            $roles = ['IS_AUTHENTICATED_FULLY'];
70
        } else {
71
            // Otherwise, test against IS_AUTHENTICATED_ANONYMOUSLY
72
            $roles = ['IS_AUTHENTICATED_ANONYMOUSLY'];
73
        }
74
75
        foreach ($attributes as $attribute) {
76
            if (!$this->supportsAttribute($attribute)) {
77
                continue;
78
            }
79
80
            if ($rbac = $operation->getExtension('rbac')) {
81
                $roles = $this->normalizeRoleNames($rbac);
82
            }
83
84
            if ($this->decisionManager->decide($token, $roles)) {
85
                $vote = VoterInterface::ACCESS_GRANTED;
86
            } else {
87
                $vote = VoterInterface::ACCESS_DENIED;
88
            }
89
        }
90
91
        return $vote;
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97
    public function supportsAttribute($attribute)
98
    {
99
        return $attribute === RequestAuthorizationListener::ATTRIBUTE;
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function supportsClass($class)
0 ignored issues
show
Unused Code introduced by
The parameter $class is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
106
    {
107
        return false;
108
    }
109
110
    /**
111
     * @param array|string $roleNames
112
     * @return array
113
     */
114
    private function normalizeRoleNames($roleNames): array
115
    {
116
        $roleNames = !is_array($roleNames) ? [$roleNames] : $roleNames;
117
        foreach ($roleNames as &$roleName) {
118
            $roleName = strtoupper($roleName);
119
            if (0 !== strpos($roleName, 'ROLE_')) {
120
                $roleName = "ROLE_$roleName";
121
            }
122
        }
123
124
        return $roleNames;
125
    }
126
}