Completed
Push — develop ( 784583...9a84aa )
by Michiel
02:05 queued 10s
created

AllowedInOtherInstitutionVoter::authorizedByRole()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 16
Ratio 100 %

Importance

Changes 0
Metric Value
dl 16
loc 16
rs 9.7333
c 0
b 0
f 0
cc 1
nc 1
nop 1
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\Security\Authorization\Context\InstitutionContext;
23
use Surfnet\StepupRa\RaBundle\Service\InstitutionConfigurationOptionsServiceInterface;
24
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
25
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
26
use Symfony\Component\Security\Core\Role\Role;
27
28
/**
29
 * Given a InstitutionContext, votes if allowed to perform actions on
30
 * a target institution (the institution of the identity we are performing actions on).
31
 */
32
class AllowedInOtherInstitutionVoter implements VoterInterface
33
{
34
    const VIEW_AUDITLOG = 'view_auditlog';
35
36
    private $service;
37
38
    public function __construct(InstitutionConfigurationOptionsServiceInterface $service)
39
    {
40
        $this->service = $service;
41
    }
42
43
    /**
44
     * @SuppressWarnings(PHPMD.CyclomaticComplexity) - many simple tests are required to ascertain if the action is
45
     * allowed
46
     *
47
     * @param TokenInterface $token A TokenInterface instance
48
     * @param InstitutionContext $subject The subject to secure
49
     * @param array $attributes An array of attributes associated with the method being invoked
50
     * @return int either ACCESS_GRANTED, ACCESS_ABSTAIN, or ACCESS_DENIED
51
     */
52
    public function vote(TokenInterface $token, $subject, array $attributes)
53
    {
54
        // Check if the class of this object is supported by this voter
55
        if (!$this->supportsClass(get_class($subject))) {
56
            return VoterInterface::ACCESS_ABSTAIN;
57
        }
58
59
        // This voter allows one attribute to vote on.
60
        if (count($attributes) > 1) {
61
            throw new InvalidArgumentException('Only one attribute is allowed');
62
        }
63
64
        $attribute = $attributes[0];
65
66
        // Check if the given attribute is covered by this voter
67
        if (!$this->supportsAttribute($attribute)) {
68
            return VoterInterface::ACCESS_ABSTAIN;
69
        }
70
71
        $actorRoles = $token->getRoles();
72
73
        // Does the actor have one of the required roles?
74
        if (!$this->authorizedByRole($actorRoles)) {
75
            return VoterInterface::ACCESS_DENIED;
76
        }
77
78
        $institutionConfig = $this->service->getInstitutionConfigurationOptionsFor($subject->getActorInstitution());
79
80
        if (!$institutionConfig) {
81
            return VoterInterface::ACCESS_ABSTAIN;
82
        }
83
84
        $raInstitutions = $institutionConfig->useRa;
85
        $raaInstitutions = $institutionConfig->useRaa;
86
87
        // Now test if any of the roles allow the user to perform the requested task
88
        foreach ($actorRoles as $role) {
89
            switch ($role->getRole()) {
90
                // The SRAA role is always allowed to perform the VIEW_AUDITLOG action
91
                case "ROLE_SRAA":
92
                    return VoterInterface::ACCESS_GRANTED;
93
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
94
                case "ROLE_RA":
95
                    // RA roles are allowed if the target institution is in the useRa options.
96
                    if (in_array($subject->getTargetInstitution(), $raInstitutions)) {
97
                        return VoterInterface::ACCESS_GRANTED;
98
                    }
99
                    break;
100
                case "ROLE_RAA":
101
                    // (S)RAA roles are allowed if either the target institution is in the useRa or useRaa options.
102
                    if (in_array($subject->getTargetInstitution(), array_merge($raInstitutions, $raaInstitutions))) {
103
                        return VoterInterface::ACCESS_GRANTED;
104
                    }
105
                    break;
106
            }
107
        }
108
109
        return VoterInterface::ACCESS_DENIED;
110
    }
111
112
    private function supportsAttribute($attribute)
113
    {
114
        return in_array($attribute, [self::VIEW_AUDITLOG]);
115
    }
116
117
    private function supportsClass($class)
118
    {
119
        $supportedClass = InstitutionContext::class;
120
121
        return $supportedClass === $class;
122
    }
123
124 View Code Duplication
    private function authorizedByRole(array $roles)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
125
    {
126
        // The role requirements to VIEW_AUDITLOG, one of these roles must be met
127
        $allowedRoles = ['ROLE_SRAA', 'ROLE_RAA', 'ROLE_RA'];
128
129
        // Convert the Role[] to an array of strings representing the role names.
130
        $roles = array_map(
131
            function (Role $role) {
132
                return $role->getRole();
133
            },
134
            $roles
135
        );
136
137
        // And test if there is an intersection (is one or more of the token roles also in the allowed roles)
138
        return count(array_intersect($roles, $allowedRoles)) > 0;
139
    }
140
}
141