Failed Conditions
Push — issue#767 ( 20b3b1 )
by Guilherme
10:48
created

CompleteUserInfoTaskValidator::checkScope()   D

Complexity

Conditions 17
Paths 22

Size

Total Lines 35
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 17

Importance

Changes 0
Metric Value
cc 17
eloc 28
nc 22
nop 2
dl 0
loc 35
ccs 26
cts 26
cp 1
crap 17
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\Task;
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\OAuthBundle\Model\ClientInterface;
19
use LoginCidadao\ValidationBundle\Validator\Constraints\CPFValidator;
20
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
21
use Symfony\Component\HttpFoundation\Request;
22
23
class CompleteUserInfoTaskValidator
24
{
25
    /** @var EventDispatcherInterface */
26
    private $dispatcher;
27
28
    /**
29
     * Allow the CompleteUserInfoTask to be skipped if the user has already authorized the RP and the RP didn't ask
30
     * for the Task explicitly.
31
     * @var bool
32
     */
33
    private $skipIfAuthorized;
34
35
    /**
36
     * CompleteUserInfoTaskValidator constructor.
37
     * @param EventDispatcherInterface $dispatcher
38
     * @param bool $skipIfAuthorized
39
     */
40 9
    public function __construct(EventDispatcherInterface $dispatcher, $skipIfAuthorized)
41
    {
42 9
        $this->dispatcher = $dispatcher;
43 9
        $this->skipIfAuthorized = $skipIfAuthorized;
44 9
    }
45
46
    /**
47
     * Check if prompt=consent was requested (also checks the Nonce)
48
     *
49
     * @param Request $request
50
     * @return bool
51
     */
52 4
    public function shouldPromptConsent(Request $request)
53
    {
54 4
        return $request->get('prompt', null) === 'consent' && $this->isNonceValid($request);
55
    }
56
57
    /**
58
     * Checks if the Client/RP already has the user's authorization.
59
     * @param PersonInterface $person
60
     * @param ClientInterface $client
61
     * @return bool
62
     */
63 2
    public function isClientAuthorized(PersonInterface $person, ClientInterface $client)
64
    {
65
        /** @var OAuthEvent $event */
66 2
        $event = $this->dispatcher->dispatch(
67 2
            OAuthEvent::PRE_AUTHORIZATION_PROCESS,
68 2
            new OAuthEvent($person, $client)
69
        );
70
71 2
        return $event->isAuthorizedClient();
72
    }
73
74
    /**
75
     * Check if the Request's route is valid for this Task
76
     * @param Request $request
77
     * @return bool
78
     */
79 5
    public function isRouteValid(Request $request)
80
    {
81 5
        $route = $request->get('_route');
82 5
        $scopes = $request->get('scope', false);
83
84 5
        return $route === '_authorize_validate' && false !== $scopes;
85
    }
86
87
    /**
88
     * Get the CompleteUserInfoTask for the $user's missing data
89
     *
90
     * @param PersonInterface $user
91
     * @param ClientInterface $client
92
     * @param Request $request
93
     * @return CompleteUserInfoTask|null
94
     */
95 3
    public function getCompleteUserInfoTask(PersonInterface $user, ClientInterface $client, Request $request)
96
    {
97 3
        if (!$this->isRouteValid($request) || $this->canSkipTask($request, $user, $client)) {
98 1
            return null;
99
        }
100
101 2
        $emptyClaims = [];
102 2
        $scopes = explode(' ', $request->get('scope', false));
0 ignored issues
show
Bug introduced by
It seems like $request->get('scope', false) can also be of type false; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

102
        $scopes = explode(' ', /** @scrutinizer ignore-type */ $request->get('scope', false));
Loading history...
103 2
        foreach ($scopes as $scope) {
104 2
            if (false === $this->checkScope($user, $scope)) {
105 2
                $emptyClaims[] = $scope;
106
            }
107
        }
108
109 2
        if (empty($emptyClaims) > 0) {
110 1
            return null;
111
        }
112
113 1
        return new CompleteUserInfoTask($client->getPublicId(), $emptyClaims, $request->get('nonce'));
114
    }
115
116
    /**
117
     * @param Request $request
118
     * @return bool
119
     */
120 2
    private function isNonceValid(Request $request)
121
    {
122 2
        $nonce = $request->get('nonce', null);
123
124
        // TODO: check is nonce was already used
125
126 2
        return $nonce !== null;
127
    }
128
129
    /**
130
     * Check if scope's data is missing from $user
131
     *
132
     * @param PersonInterface $user
133
     * @param string $scope
134
     * @return bool
135
     */
136 2
    private function checkScope(PersonInterface $user, $scope)
137
    {
138
        // 'id_cards', 'addresses'
139
        switch ($scope) {
140 2
            case 'name':
141 2
            case 'full_name':
142 2
            case 'surname':
143 1
                $value = $user->getFullName();
144
145 1
                return $value && strlen($value) > 0 && strlen($user->getSurname()) > 0;
146
                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...
147 2
            case 'mobile':
148 2
            case 'phone_number':
149 1
                $value = $user->getMobile();
150 1
                break;
151 2
            case 'country':
152 1
                return $user->getCountry() instanceof Country;
153 2
            case 'state':
154 1
                return $user->getState() instanceof State;
155 2
            case 'city':
156 1
                return $user->getCity() instanceof City;
157 2
            case 'birthdate':
158 1
                return $user->getBirthdate() instanceof \DateTime;
159 2
            case 'email':
160 2
            case 'email_verified':
161 1
                return $user->getEmailConfirmedAt() instanceof \DateTime;
162 2
            case 'cpf':
163 1
                $cpf = $user->getCpf();
164
165 1
                return $cpf && CPFValidator::isCPFValid($cpf);
166
            default:
167 2
                return true;
168
        }
169
170 1
        return $value && strlen($value) > 0;
171
    }
172
173
    /**
174
     * Check if this Task can be skipped based on an existing Authorization
175
     *
176
     * @param Request $request
177
     * @param PersonInterface $user
178
     * @param ClientInterface $client
179
     * @return bool
180
     */
181 3
    private function canSkipTask(Request $request, PersonInterface $user, ClientInterface $client)
182
    {
183
        // Can this Task be skipped if the RP is already Authorized?
184 3
        if ($this->skipIfAuthorized) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
185
186
            // To force this task's execution, the RP MUST send prompt=consent and a nonce value.
187 1
            $shouldPromptConsent = $this->shouldPromptConsent($request);
188 1
            $isAuthorized = $this->isClientAuthorized($user, $client);
189
190
            // Skip if the RP is authorized and the Task wasn't explicitly requested
191 1
            if ($isAuthorized && !$shouldPromptConsent) {
192 1
                return true;
193
            }
194
        }
195
196 2
        return false;
197
    }
198
}
199