Failed Conditions
Push — issue#666 ( 82e9d5...91903a )
by Guilherme
08:00
created

TaskSubscriber::checkScope()   D

Complexity

Conditions 17
Paths 22

Size

Total Lines 35
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 306

Importance

Changes 0
Metric Value
cc 17
eloc 28
nc 22
nop 2
dl 0
loc 35
ccs 0
cts 26
cp 0
crap 306
rs 4.9807
c 0
b 0
f 0

How to fix   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
 * This file is part of the login-cidadao project or it's bundles.
4
 *
5
 * (c) Guilherme Donato <guilhermednt on github>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace LoginCidadao\OpenIDBundle\EventListener;
12
13
use FOS\OAuthServerBundle\Event\OAuthEvent;
14
use LoginCidadao\CoreBundle\Entity\City;
15
use LoginCidadao\CoreBundle\Entity\Country;
16
use LoginCidadao\CoreBundle\Entity\State;
17
use LoginCidadao\CoreBundle\Model\PersonInterface;
18
use LoginCidadao\OpenIDBundle\Manager\ClientManager;
19
use LoginCidadao\OpenIDBundle\Task\CompleteUserInfoTask;
20
use LoginCidadao\TaskStackBundle\Event\GetTasksEvent;
21
use LoginCidadao\TaskStackBundle\TaskStackEvents;
22
use LoginCidadao\ValidationBundle\Validator\Constraints\CPFValidator;
23
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
24
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
25
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
26
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
27
use Symfony\Component\Security\Http\HttpUtils;
28
29
class TaskSubscriber implements EventSubscriberInterface
30
{
31
    /** @var TokenStorage */
32
    protected $tokenStorage;
33
34
    /** @var AuthorizationCheckerInterface */
35
    protected $authChecker;
36
37
    /** @var HttpUtils */
38
    protected $httpUtils;
39
40
    /** @var ClientManager */
41
    private $clientManager;
42
43
    /** @var bool */
44
    private $skipCompletionTaskIfAuthorized;
45
46
    /**
47
     * TaskSubscriber constructor.
48
     * @param TokenStorage $tokenStorage
49
     * @param AuthorizationCheckerInterface $authChecker
50
     * @param HttpUtils $httpUtils
51
     * @param ClientManager $clientManager
52
     */
53
    public function __construct(
54
        TokenStorage $tokenStorage,
55
        AuthorizationCheckerInterface $authChecker,
56
        HttpUtils $httpUtils,
57
        ClientManager $clientManager
58
    ) {
59
        $this->tokenStorage = $tokenStorage;
60
        $this->authChecker = $authChecker;
61
        $this->httpUtils = $httpUtils;
62
        $this->clientManager = $clientManager;
63
    }
64
65
66 14
    public static function getSubscribedEvents()
67
    {
68
        return [
69 14
            TaskStackEvents::GET_TASKS => ['onGetTasks', 50],
70
        ];
71
    }
72
73
    public function onGetTasks(GetTasksEvent $event, $eventName, EventDispatcherInterface $dispatcher)
0 ignored issues
show
Unused Code introduced by
The parameter $eventName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

73
    public function onGetTasks(GetTasksEvent $event, /** @scrutinizer ignore-unused */ $eventName, EventDispatcherInterface $dispatcher)

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

Loading history...
74
    {
75
        try {
76
            /** @var PersonInterface $user */
77
            $user = $this->tokenStorage->getToken()->getUser();
78
79
            if (!$user instanceof PersonInterface) {
0 ignored issues
show
introduced by
The condition ! $user instanceof Login...e\Model\PersonInterface can never be true.
Loading history...
80
                return;
81
            }
82
        } catch (\Exception $e) {
83
            return;
84
        }
85
86
        $request = $event->getRequest();
87
88
        $route = $request->get('_route');
89
        $scopes = $request->get('scope', false);
90
        if ($route !== '_authorize_validate' || !$scopes) {
91
            return;
92
        }
93
94
        $clientId = $request->get('client_id', $request->attributes->get('clientId'));
95
96
        // To force this task's execution, the RP MUST send prompt=consent and a nonce value.
97
        $promptConsent = $request->get('prompt', null) == 'consent'
98
            && $request->get('nonce', null) !== null;
99
100
        if (!$clientId) {
101
            return;
102
        }
103
        if ($this->skipCompletionTaskIfAuthorized
104
            && $this->isAuthorizedClient($dispatcher, $clientId)
105
            && !$promptConsent
106
        ) {
107
            return;
108
        }
109
110
        $scopes = explode(' ', $scopes);
111
        $emptyClaims = [];
112
        foreach ($scopes as $scope) {
113
            if ($this->checkScope($user, $scope)) {
114
                continue;
115
            }
116
            $emptyClaims[] = $scope;
117
        }
118
119
        if (count($emptyClaims) > 0) {
120
            $task = new CompleteUserInfoTask($clientId, $emptyClaims, $request->get('nonce'));
121
            $event->addTask($task);
122
        }
123
    }
124
125
    /**
126
     * @param PersonInterface $user
127
     * @param string $scope
128
     * @return bool
129
     */
130
    private function checkScope(PersonInterface $user, $scope)
131
    {
132
        // 'id_cards', 'addresses'
133
        switch ($scope) {
134
            case 'name':
135
            case 'full_name':
136
            case 'surname':
137
                $value = $user->getFullName();
138
139
                return $value && strlen($value) > 0 && strlen($user->getSurname()) > 0;
140
                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...
141
            case 'mobile':
142
            case 'phone_number':
143
                $value = $user->getMobile();
144
                break;
145
            case 'country':
146
                return $user->getCountry() instanceof Country;
147
            case 'state':
148
                return $user->getState() instanceof State;
149
            case 'city':
150
                return $user->getCity() instanceof City;
151
            case 'birthdate':
152
                return $user->getBirthdate() instanceof \DateTime;
153
            case 'email':
154
            case 'email_verified':
155
                return $user->getEmailConfirmedAt() instanceof \DateTime;
156
            case 'cpf':
157
                $cpf = $user->getCpf();
158
159
                return $cpf && CPFValidator::isCPFValid($cpf);
160
            default:
161
                return true;
162
        }
163
164
        return $value && strlen($value) > 0;
165
    }
166
167
    private function isAuthorizedClient(EventDispatcherInterface $dispatcher, $clientId)
168
    {
169
        $client = $this->clientManager->getClientById($clientId);
170
171
        /** @var OAuthEvent $event */
172
        $event = $dispatcher->dispatch(
173
            OAuthEvent::PRE_AUTHORIZATION_PROCESS,
174
            new OAuthEvent($this->tokenStorage->getToken()->getUser(), $client)
175
        );
176
177
        return $event->isAuthorizedClient();
178
    }
179
180
    public function setSkipCompletionTaskIfAuthorized($skip)
181
    {
182
        $this->skipCompletionTaskIfAuthorized = $skip;
183
    }
184
}
185