Failed Conditions
Push — issue#666 ( f415d0...521a08 )
by Guilherme
12:02
created

isClientAuthorized()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 1
rs 9.6666
c 0
b 0
f 0
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)
98 3
            || $this->canSkipTask($request, $user, $client)
99 3
            || false === $scope = $request->get('scope', false)) {
100 1
            return null;
101
        }
102
103 2
        $scopes = explode(' ', $scope);
104 2
        $emptyClaims = array_intersect($scopes, $this->checkScopes($user));
105
106 2
        if (empty($emptyClaims) > 0) {
107 1
            return null;
108
        }
109
110 1
        return new CompleteUserInfoTask($client->getPublicId(), $emptyClaims, $request->get('nonce'));
111
    }
112
113
    /**
114
     * @param Request $request
115
     * @return bool
116
     */
117 2
    private function isNonceValid(Request $request)
118
    {
119 2
        $nonce = $request->get('nonce', null);
120
121
        // TODO: check is nonce was already used
122
123 2
        return $nonce !== null;
124
    }
125
126 2
    private function checkScopes(PersonInterface $user)
127
    {
128 2
        $fullName = $user->getFullName();
129 2
        $missingScope = $this->setSynonyms([
130 2
            'full_name' => $this->isFilled($fullName) && $this->isFilled($user->getSurname()),
131 2
            'phone_number' => $this->isFilled($user->getMobile()),
132 2
            'country' => $user->getCountry() instanceof Country,
133 2
            'state' => $user->getState() instanceof State,
134 2
            'city' => $user->getCity() instanceof City,
135 2
            'birthday' => $user->getBirthdate() instanceof \DateTime,
136 2
            'email_verified' => $user->getEmailConfirmedAt() instanceof \DateTime,
137 2
            'cpf' => $this->isFilled($user->getCpf()) && CPFValidator::isCPFValid($user->getCpf()),
138
        ], [
139 2
            'name' => 'full_name',
140
            'birthdate' => 'birthday',
141
            'surname' => 'full_name',
142
            'mobile' => 'phone_number',
143
            'email' => 'email_verified',
144
        ]);
145
146 2
        return array_keys(
147 2
            array_filter($missingScope, function ($value) {
148 2
                return !$value;
149 2
            })
150
        );
151
    }
152
153 2
    private function isFilled($value)
154
    {
155 2
        return $value && strlen($value) > 0;
156
    }
157
158 2
    private function setSynonyms(array $data, array $synonyms)
159
    {
160 2
        foreach ($synonyms as $synonym => $original) {
161 2
            $data[$synonym] = $data[$original];
162
        }
163
164 2
        return $data;
165
    }
166
167
    /**
168
     * Check if this Task can be skipped based on an existing Authorization
169
     *
170
     * @param Request $request
171
     * @param PersonInterface $user
172
     * @param ClientInterface $client
173
     * @return bool
174
     */
175 3
    private function canSkipTask(Request $request, PersonInterface $user, ClientInterface $client)
176
    {
177
        // Can this Task be skipped if the RP is already Authorized?
178 3
        if ($this->skipIfAuthorized) {
179
            // To force this task's execution, the RP MUST send prompt=consent and a nonce value.
180 1
            $shouldPromptConsent = $this->shouldPromptConsent($request);
181 1
            $isAuthorized = $this->isClientAuthorized($user, $client);
182
183
            // Skip if the RP is authorized and the Task wasn't explicitly requested
184 1
            if ($isAuthorized && !$shouldPromptConsent) {
185 1
                return true;
186
            }
187
        }
188
189 2
        return false;
190
    }
191
}
192