RelatedObjectVoter::getUserRoles()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 10
rs 9.4285
1
<?php
2
3
namespace Dominikzogg\EnergyCalculator\Voter;
4
5
use Psr\Log\LoggerInterface;
6
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
7
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
8
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
9
use Symfony\Component\Security\Core\User\UserInterface;
10
11
class RelatedObjectVoter implements VoterInterface
12
{
13
    /**
14
     * @var RoleHierarchyInterface
15
     */
16
    protected $roleHierarchy;
17
18
    /**
19
     * @var LoggerInterface
20
     */
21
    protected $logger;
22
23
    /**
24
     * @param RoleHierarchyInterface $roleHierarchy
25
     * @param LoggerInterface        $logger
26
     */
27
    public function __construct(RoleHierarchyInterface $roleHierarchy, LoggerInterface $logger)
28
    {
29
        $this->roleHierarchy = $roleHierarchy;
30
        $this->logger = $logger;
31
    }
32
33
    /**
34
     * @param  string $attribute
35
     * @return bool
36
     */
37
    public function supportsAttribute($attribute)
0 ignored issues
show
Unused Code introduced by
The parameter $attribute 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...
38
    {
39
        return true;
40
    }
41
42
    /**
43
     * @param  string $class
44
     * @return bool
45
     */
46
    public function supportsClass($class)
47
    {
48
        try {
49
            $reflection = new \ReflectionClass($class);
50
        } catch (\Exception $e) {
51
            return false;
52
        }
53
54
        if ($reflection->implementsInterface('Dominikzogg\EnergyCalculator\Voter\RelatedObjectInterface')) {
55
            return true;
56
        }
57
58
        return false;
59
    }
60
61
    /**
62
     * @param  TokenInterface $token
63
     * @param  null|object    $object
64
     * @param  array          $attributes
65
     * @return int
66
     */
67
    public function vote(TokenInterface $token, $object, array $attributes)
68
    {
69
        $voterName = $this->getName();
70
71
        if (!is_object($object)) {
72
            $this->logger->debug(sprintf('RelatedObjectVoter %s not received an object. Voting to abstain.', $voterName));
73
74
            return self::ACCESS_ABSTAIN;
75
        }
76
77
        $user = $token->getUser();
78
        if (!$user instanceof UserInterface) {
79
            $this->logger->debug(sprintf('RelatedObjectVoter %s not received an valid user object. Voting to abstain.', $voterName));
80
81
            return self::ACCESS_ABSTAIN;
82
        }
83
84
        if (!$this->supportsClass($object)) {
0 ignored issues
show
Documentation introduced by
$object is of type object, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
85
            $objectString = is_object($object) ? get_class($object) : gettype($object);
86
            $this->logger->debug(sprintf('RelatedObjectVoter %s does not support class %s. Voting to abstain.', $voterName, $objectString));
87
88
            return self::ACCESS_ABSTAIN;
89
        }
90
91
        $userRoles = $this->getUserRoles($token);
92
        foreach ($this->getNeededRoles($attributes, $object) as $neededRole) {
93
            if (!in_array($neededRole, $userRoles)) {
94
                $this->logger->debug(sprintf('Needed Role "%s" not found on user. Voting to abstain.', $neededRole));
95
96
                return self::ACCESS_ABSTAIN;
97
            }
98
        }
99
100
        if (true === $this->isRelatedObject($user, $object)) {
0 ignored issues
show
Documentation introduced by
$user is of type object<Symfony\Component...ore\User\UserInterface>, but the function expects a object<Dominikzogg\Energ...RelatedObjectInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
101
            $this->logger->debug(sprintf('Object is RelatedObject (%s). Voting to grant access.', $voterName));
102
103
            return self::ACCESS_GRANTED;
104
        }
105
106
        $this->logger->debug(sprintf('Object is not RelatedObject (%s). Voting to abstain.', $voterName));
107
108
        return self::ACCESS_ABSTAIN;
109
    }
110
111
    /**
112
     * @param  TokenInterface $token
113
     * @return array
114
     */
115
    protected function getUserRoles(TokenInterface $token)
116
    {
117
        $roles = array();
118
119
        foreach ($this->roleHierarchy->getReachableRoles($token->getRoles()) as $role) {
120
            $roles[] = $role->getRole();
121
        }
122
123
        return array_unique($roles);
124
    }
125
126
    /**
127
     * @param  array                  $attributes
128
     * @param  RelatedObjectInterface $object
129
     * @return array
130
     */
131
    protected function getNeededRoles(array $attributes, RelatedObjectInterface $object)
132
    {
133
        $roles = array();
134
        $prefix = $this->getNeededRolesPrefix($object);
135
        foreach ($attributes as $attribute) {
136
            $roles[] = $prefix.$attribute;
137
        }
138
139
        return $roles;
140
    }
141
142
    /**
143
     * @param  RelatedObjectInterface $object
144
     * @return string
145
     */
146
    protected function getNeededRolesPrefix(RelatedObjectInterface $object)
147
    {
148
        return 'RELATED_'.strtoupper($object->getRoleNamePart()).'_';
149
    }
150
151
    /**
152
     * @param  RelatedObjectInterface $user
153
     * @param  RelatedObjectInterface $object
154
     * @return bool
155
     */
156
    protected function isRelatedObject(RelatedObjectInterface $user, RelatedObjectInterface $object)
157
    {
158
        foreach ($user->getSecurityRelatedObjects() as $usro) {
159
            foreach ($object->getSecurityRelatedObjects() as $osro) {
160
                if ($usro === $osro) {
161
                    return true;
162
                }
163
            }
164
        }
165
166
        return false;
167
    }
168
169
    /**
170
     * @return string
171
     */
172
    protected function getName()
173
    {
174
        $explode = explode("\\", get_class($this));
175
176
        return substr(end($explode), 0, -5);
177
    }
178
}
179