1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Copyright 2018 SURFnet B.V. |
5
|
|
|
* |
6
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
7
|
|
|
* you may not use this file except in compliance with the License. |
8
|
|
|
* You may obtain a copy of the License at |
9
|
|
|
* |
10
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0 |
11
|
|
|
* |
12
|
|
|
* Unless required by applicable law or agreed to in writing, software |
13
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, |
14
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15
|
|
|
* See the License for the specific language governing permissions and |
16
|
|
|
* limitations under the License. |
17
|
|
|
*/ |
18
|
|
|
|
19
|
|
|
namespace Surfnet\StepupRa\RaBundle\Security\Authorization\Voter; |
20
|
|
|
|
21
|
|
|
use InvalidArgumentException; |
22
|
|
|
use Surfnet\StepupRa\RaBundle\Service\RaListingService; |
23
|
|
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; |
24
|
|
|
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; |
25
|
|
|
use Symfony\Component\Security\Core\Role\Role; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Votes whether or not a RAA user is allowed to see the institution switcher |
29
|
|
|
* |
30
|
|
|
* The ROLE_RAA is allowed to switch institutions when (s)he is: |
31
|
|
|
* - is RAA |
32
|
|
|
* - for more than one institution |
33
|
|
|
*/ |
34
|
|
|
class AllowedToSwitchInstitutionVoter implements VoterInterface |
35
|
|
|
{ |
36
|
|
|
const RAA_SWITCHING = 'raa_switching'; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var RaListingService |
40
|
|
|
*/ |
41
|
|
|
private $service; |
42
|
|
|
|
43
|
|
|
public function __construct(RaListingService $service) |
44
|
|
|
{ |
45
|
|
|
$this->service = $service; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @param TokenInterface $token A TokenInterface instance |
50
|
|
|
* @param $subject not used |
51
|
|
|
* @param array $attributes contains the subject (triggered from twig is_granted function) |
52
|
|
|
* @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED |
53
|
|
|
*/ |
54
|
|
|
public function vote(TokenInterface $token, $subject, array $attributes) |
55
|
|
|
{ |
56
|
|
|
// Check if the class of this object is supported by this voter |
57
|
|
|
if (!$this->supportsAttribute(reset($attributes))) { |
58
|
|
|
return VoterInterface::ACCESS_ABSTAIN; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
// This voter allows one attribute to vote on. |
62
|
|
|
if (count($attributes) > 1) { |
63
|
|
|
throw new InvalidArgumentException('Only one attribute is allowed'); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
$actorRoles = $token->getRoles(); |
67
|
|
|
|
68
|
|
|
// Does the actor have one of the required roles? |
69
|
|
|
if (!$this->authorizedByRole($actorRoles)) { |
70
|
|
|
return VoterInterface::ACCESS_DENIED; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
$raListing = $this->service->searchBy($token->getUser()->id, $token->getIdentityInstitution()); |
|
|
|
|
74
|
|
|
|
75
|
|
|
if ($raListing->getTotalItems() >= 1) { |
76
|
|
|
return VoterInterface::ACCESS_GRANTED; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
return VoterInterface::ACCESS_DENIED; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
private function supportsAttribute($attribute) |
83
|
|
|
{ |
84
|
|
|
return in_array($attribute, [self::RAA_SWITCHING]); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
View Code Duplication |
private function authorizedByRole(array $roles) |
|
|
|
|
88
|
|
|
{ |
89
|
|
|
$allowedRoles = ['ROLE_SRAA', 'ROLE_RAA']; |
90
|
|
|
|
91
|
|
|
// Convert the Role[] to an array of strings representing the role names. |
92
|
|
|
$roles = array_map( |
93
|
|
|
function (Role $role) { |
94
|
|
|
return $role->getRole(); |
95
|
|
|
}, |
96
|
|
|
$roles |
97
|
|
|
); |
98
|
|
|
|
99
|
|
|
// And test if there is an intersection (is one or more of the token roles also in the allowed roles) |
100
|
|
|
return count(array_intersect($roles, $allowedRoles)) > 0; |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the interface: