Completed
Push — feature/fga-ra-management ( e1e438...be5a02 )
by Michiel
91:16 queued 70:20
created

AllowedInOtherInstitutionVoter::vote()   C

Complexity

Conditions 12
Paths 12

Size

Total Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 59
rs 6.4678
c 0
b 0
f 0
cc 12
nc 12
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
    private function authorizedByRole(array $roles)
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