Failed Conditions
Pull Request — master (#790)
by Guilherme
10:36 queued 05:09
created

Nfg::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 15
nc 1
nop 15
dl 0
loc 32
ccs 16
cts 16
cp 1
crap 1
rs 8.8571
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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 PROCERGS\LoginCidadao\NfgBundle\Service;
12
13
use Doctrine\ORM\EntityManager;
14
use FOS\UserBundle\Model\UserManagerInterface;
15
use FOS\UserBundle\Event\FilterUserResponseEvent;
16
use FOS\UserBundle\Event\FormEvent;
17
use FOS\UserBundle\Event\GetResponseUserEvent;
18
use FOS\UserBundle\Form\Factory\FormFactory;
19
use FOS\UserBundle\FOSUserEvents;
20
use FOS\UserBundle\Security\LoginManagerInterface;
21
use FOS\UserBundle\Util\CanonicalizerInterface;
22
use LoginCidadao\CoreBundle\Model\PersonInterface;
23
use PROCERGS\LoginCidadao\NfgBundle\Entity\NfgProfile;
24
use PROCERGS\LoginCidadao\CoreBundle\Entity\PersonMeuRS;
25
use PROCERGS\LoginCidadao\CoreBundle\Helper\MeuRSHelper;
26
use PROCERGS\LoginCidadao\NfgBundle\Entity\NfgProfileRepository;
27
use PROCERGS\LoginCidadao\NfgBundle\Event\GetConnectCallbackResponseEvent;
28
use PROCERGS\LoginCidadao\NfgBundle\Event\GetDisconnectCallbackResponseEvent;
29
use PROCERGS\LoginCidadao\NfgBundle\Event\GetLoginCallbackResponseEvent;
30
use PROCERGS\LoginCidadao\NfgBundle\Exception\ConnectionNotFoundException;
31
use PROCERGS\LoginCidadao\NfgBundle\Exception\CpfInUseException;
32
use PROCERGS\LoginCidadao\NfgBundle\Exception\CpfMismatchException;
33
use PROCERGS\LoginCidadao\NfgBundle\Exception\EmailInUseException;
34
use PROCERGS\LoginCidadao\NfgBundle\Exception\MissingRequiredInformationException;
35
use PROCERGS\LoginCidadao\NfgBundle\Exception\NfgAccountCollisionException;
36
use PROCERGS\LoginCidadao\NfgBundle\Exception\NfgServiceUnavailableException;
37
use PROCERGS\LoginCidadao\NfgBundle\Exception\OverrideResponseException;
38
use PROCERGS\LoginCidadao\NfgBundle\Helper\UrlHelper;
39
use PROCERGS\LoginCidadao\NfgBundle\Mailer\MailerInterface;
40
use PROCERGS\LoginCidadao\NfgBundle\NfgEvents;
41
use PROCERGS\LoginCidadao\NfgBundle\Traits\CircuitBreakerAwareTrait;
42
use Psr\Log\LoggerAwareInterface;
43
use Psr\Log\LoggerInterface;
44
use Ramsey\Uuid\Uuid;
45
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
46
use Symfony\Component\HttpFoundation\JsonResponse;
47
use Symfony\Component\HttpFoundation\RedirectResponse;
48
use Symfony\Component\HttpFoundation\Request;
49
use Symfony\Component\HttpFoundation\Response;
50
use Symfony\Component\HttpFoundation\Session\SessionInterface;
51
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
52
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
53
use Symfony\Component\Routing\RouterInterface;
54
use Symfony\Component\Security\Core\Exception\AccountStatusException;
55
56
class Nfg implements LoggerAwareInterface
57
{
58
    use CircuitBreakerAwareTrait {
59
        reportSuccess as traitReportSuccess;
60
        reportFailure as traitReportFailure;
61
    }
62
63
    /**
64
     * Key used to store the NFG AccessID in session
65
     */
66
    const ACCESS_ID_SESSION_KEY = 'nfg.access_id';
67
68
    /** @var EntityManager */
69
    private $em;
70
71
    /** @var NfgSoapInterface */
72
    private $nfgSoap;
73
74
    /** @var RouterInterface */
75
    private $router;
76
77
    /** @var SessionInterface */
78
    private $session;
79
80
    /** @var LoginManagerInterface */
81
    private $loginManager;
82
83
    /** @var MeuRSHelper */
84
    private $meuRSHelper;
85
86
    /** @var LoggerInterface */
87
    private $logger;
88
89
    /** @var string */
90
    private $loginEndpoint;
91
92
    /** @var string */
93
    private $authorizationEndpoint;
94
95
    /** @var string */
96
    private $firewallName;
97
98
    /** @var UserManagerInterface */
99
    private $userManager;
100
101
    /** @var EventDispatcherInterface */
102
    private $dispatcher;
103
104
    /** @var FormFactory */
105
    private $formFactory;
106
107
    /** @var NfgProfileRepository */
108
    private $nfgProfileRepository;
109
110
    /** @var MailerInterface */
111
    private $mailer;
112
113
    /** @var CanonicalizerInterface */
114
    private $emailCanonicalizer;
115
116 31
    public function __construct(
117
        EntityManager $em,
118
        NfgSoapInterface $client,
119
        RouterInterface $router,
120
        SessionInterface $session,
121
        LoginManagerInterface $loginManager,
122
        MeuRSHelper $meuRSHelper,
123
        EventDispatcherInterface $dispatcher,
124
        UserManagerInterface $userManager,
125
        FormFactory $formFactory,
126
        NfgProfileRepository $nfgProfileRepository,
127
        MailerInterface $mailer,
128
        CanonicalizerInterface $emailCanonicalizer,
129
        $firewallName,
130
        $loginEndpoint,
131
        $authorizationEndpoint
132
    ) {
133 31
        $this->em = $em;
134 31
        $this->nfgSoap = $client;
135 31
        $this->router = $router;
136 31
        $this->session = $session;
137 31
        $this->loginManager = $loginManager;
138 31
        $this->meuRSHelper = $meuRSHelper;
139 31
        $this->dispatcher = $dispatcher;
140 31
        $this->userManager = $userManager;
141 31
        $this->formFactory = $formFactory;
142 31
        $this->nfgProfileRepository = $nfgProfileRepository;
143 31
        $this->mailer = $mailer;
144 31
        $this->emailCanonicalizer = $emailCanonicalizer;
145 31
        $this->firewallName = $firewallName;
146 31
        $this->loginEndpoint = $loginEndpoint;
147 31
        $this->authorizationEndpoint = $authorizationEndpoint;
148 31
    }
149
150
    /**
151
     * @return string
152
     * @throws NfgServiceUnavailableException
153
     */
154 5
    private function getAccessId()
155
    {
156 5
        $nfgSoap = $this->nfgSoap;
157
158 5
        return $this->protect(function () use ($nfgSoap) {
159
            try {
160 3
                $accessId = $nfgSoap->getAccessID();
161
162 1
                return $accessId;
163 2
            } catch (NfgServiceUnavailableException $e) {
164 1
                throw $e;
165 1
            } catch (\Exception $e) {
166 1
                throw new NfgServiceUnavailableException($e->getMessage(), 500, $e);
167
            }
168 5
        });
169
    }
170
171
    /**
172
     * @param string $accessToken
173
     * @param string|null $voterRegistration
174
     * @param bool $testRequiredInfo
175
     * @return NfgProfile
176
     */
177 18
    public function getUserInfo($accessToken, $voterRegistration = null, $testRequiredInfo = true)
178
    {
179 18
        $nfgSoap = $this->nfgSoap;
180
181
        try {
182 18
            $nfgProfile = $this->protect(function () use ($nfgSoap, $accessToken, $voterRegistration) {
0 ignored issues
show
Unused Code introduced by
The import $nfgSoap is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
183 17
                return $this->nfgSoap->getUserInfo($accessToken, $voterRegistration);
184 18
            });
185 3
        } catch (NfgServiceUnavailableException $e) {
186 2
            throw $e;
187 1
        } catch (\Exception $e) {
188 1
            throw new NfgServiceUnavailableException($e->getMessage(), 500, $e);
189
        }
190
191 15
        $requiredInfo = [$nfgProfile->getName(), $nfgProfile->getCpf(), $nfgProfile->getEmail()];
192 15
        $missingRequiredInfo = array_search(null, $requiredInfo);
193
194 15
        if ($testRequiredInfo && false !== $missingRequiredInfo) {
195 2
            throw new MissingRequiredInformationException('Some needed information was not authorized on NFG.');
196
        }
197
198 13
        return $nfgProfile;
199
    }
200
201
    /**
202
     * @return JsonResponse
203
     */
204 4
    public function login()
205
    {
206 4
        return $this->redirect($this->loginEndpoint, 'nfg_login_callback');
207
    }
208
209 6
    public function loginCallback(array $params, $secret)
210
    {
211 6
        $cpf = array_key_exists('cpf', $params) ? $params['cpf'] : null;
212 6
        $accessId = array_key_exists('accessId', $params) ? $params['accessId'] : null;
213 6
        $prsec = array_key_exists('prsec', $params) ? $params['prsec'] : null;
214
215 6
        if (!$cpf || !$accessId || !$prsec) {
216 1
            throw new BadRequestHttpException('Missing CPF, AccessID or PRSEC');
217
        }
218
219 5
        $signature = hash_hmac('sha256', "$cpf$accessId", $secret);
220 5
        if (!$signature || strcmp(strtolower($signature), strtolower($prsec)) !== 0) {
221 1
            throw new AccessDeniedHttpException('Invalid PRSEC signature.');
222
        }
223
224 4
        if ($this->session->get(self::ACCESS_ID_SESSION_KEY) !== $accessId) {
225 1
            throw new AccessDeniedHttpException('Invalid AccessID');
226
        }
227
228
        /** @var PersonInterface $user */
229 3
        $personMeuRS = $this->meuRSHelper->getPersonByCpf($this->sanitizeCpf($cpf), true);
230
231 3
        if (!$personMeuRS || !$personMeuRS->getPerson() || !$personMeuRS->getNfgAccessToken()) {
232 1
            throw new ConnectionNotFoundException('No user found matching this CPF');
233
        }
234 2
        $user = $personMeuRS->getPerson();
235
236 2
        return $this->logInUser($user, $params);
237
    }
238
239 1
    public function connect()
240
    {
241 1
        return $this->redirect($this->authorizationEndpoint, 'nfg_connect_callback');
242
    }
243
244
    /**
245
     * @param Request $request
246
     * @param PersonMeuRS $personMeuRS
247
     * @param bool $overrideExisting
248
     * @return Response
249
     */
250 19
    public function connectCallback(Request $request, PersonMeuRS $personMeuRS, $overrideExisting = false)
251
    {
252 19
        $response = null;
253 19
        $accessToken = $request->get('paccessid');
254 19
        if (!$accessToken) {
255 1
            throw new BadRequestHttpException("Missing paccessid parameter");
256
        }
257
258 18
        $nfgProfile = $this->getUserInfo($accessToken, $personMeuRS->getVoterRegistration());
259
260 13
        if (!($personMeuRS->getPerson() instanceof PersonInterface)) {
0 ignored issues
show
introduced by
$personMeuRS->getPerson() is always a sub-type of LoginCidadao\CoreBundle\Model\PersonInterface.
Loading history...
261
            try {
262 7
                $response = $this->register($request, $personMeuRS, $nfgProfile);
263 5
            } catch (OverrideResponseException $e) {
264 3
                $event = new GetConnectCallbackResponseEvent(
265 3
                    $request, $personMeuRS, $overrideExisting, $e->getResponse()
266
                );
267 3
                $this->dispatcher->dispatch(NfgEvents::CONNECT_CALLBACK_RESPONSE, $event);
268
269 3
                return $event->getResponse();
270
            }
271
        }
272
273 8
        $sanitizedCpf = $this->sanitizeCpf($nfgProfile->getCpf());
274 8
        if (!$personMeuRS->getPerson()->getCpf()) {
275 3
            $personMeuRS->getPerson()->setCpf($sanitizedCpf);
276
        }
277
278
        try {
279 8
            $this->checkCpf($personMeuRS, $nfgProfile, $overrideExisting);
280 2
        } catch (NfgAccountCollisionException $e) {
281 1
            $e->setAccessToken($accessToken);
282 1
            throw $e;
283
        }
284
285 6
        $nfgProfile = $this->syncNfgProfile($nfgProfile);
286
287 6
        $this->em->persist($nfgProfile);
288 6
        $personMeuRS->setNfgProfile($nfgProfile);
289 6
        $personMeuRS->setNfgAccessToken($accessToken);
290 6
        $this->em->flush();
291
292 6
        if (!$response) {
0 ignored issues
show
introduced by
$response is of type null, thus it always evaluated to false.
Loading history...
293 4
            $response = new RedirectResponse($this->router->generate('fos_user_profile_edit'));
294
        }
295
296 6
        $event = new GetConnectCallbackResponseEvent($request, $personMeuRS, $overrideExisting, $response);
297 6
        $this->dispatcher->dispatch(NfgEvents::CONNECT_CALLBACK_RESPONSE, $event);
298
299 6
        return $event->getResponse();
300
    }
301
302
    /**
303
     * @param PersonMeuRS $personMeuRS
304
     * @return Response
305
     */
306 2
    public function disconnect(PersonMeuRS $personMeuRS)
307
    {
308 2
        if ($personMeuRS->getNfgProfile()) {
309 2
            $this->em->remove($personMeuRS->getNfgProfile());
310 2
            $personMeuRS->setNfgAccessToken(null);
311 2
            $personMeuRS->setNfgProfile(null);
312 2
            $this->em->flush();
313
        }
314
315 2
        $response = new RedirectResponse($this->router->generate('fos_user_profile_edit'));
316 2
        $event = new GetDisconnectCallbackResponseEvent($personMeuRS, $response);
317 2
        $this->dispatcher->dispatch(NfgEvents::DISCONNECT_CALLBACK_RESPONSE, $event);
318
319 2
        return $event->getResponse();
320
    }
321
322 5
    private function redirect($endpoint, $callbackRoute)
323
    {
324 5
        $accessId = $this->getAccessId();
325 1
        $this->session->set(self::ACCESS_ID_SESSION_KEY, $accessId);
326 1
        $callbackUrl = $this->router->generate($callbackRoute, [], RouterInterface::ABSOLUTE_URL);
327
328 1
        $url = parse_url($endpoint);
329 1
        $url['query'] = UrlHelper::addToQuery(
330
            [
331 1
                'accessid' => $accessId,
332 1
                'urlretorno' => $callbackUrl,
333
            ],
334 1
            isset($url['query']) ? $url['query'] : null
335
        );
336
337 1
        return new JsonResponse(['target' => http_build_url($url)]);
338
    }
339
340
    /**
341
     * Sets a logger instance on the object.
342
     *
343
     * @param LoggerInterface $logger
344
     *
345
     * @return void
346
     */
347 2
    public function setLogger(LoggerInterface $logger)
348
    {
349 2
        $this->logger = $logger;
350 2
    }
351
352 15
    private function sanitizeCpf($cpf)
353
    {
354 15
        return str_pad(preg_replace('/[^0-9]/', '', $cpf), 11, '0', STR_PAD_LEFT);
355
    }
356
357
    /**
358
     * @param PersonMeuRS $personMeuRS
359
     * @param NfgProfile $nfgProfile
360
     * @param bool $overrideExisting
361
     */
362 8
    private function checkCpf(PersonMeuRS $personMeuRS, NfgProfile $nfgProfile, $overrideExisting = false)
363
    {
364 8
        $person = $personMeuRS->getPerson();
365
366
        // Check data inconsistency
367 8
        if ($person->getCpf() !== $this->sanitizeCpf($nfgProfile->getCpf())) {
368 1
            throw new CpfMismatchException("User's CPF doesn't match CPF from NFG.");
369
        }
370
371
        // Check CPF collision
372 7
        $otherPerson = $this->meuRSHelper->getPersonByCpf($person->getCpf(), true);
373 7
        if (null === $otherPerson || $otherPerson->getId() === $personMeuRS->getId()) {
374
            // No collision found. We're good! :)
375 4
            return;
376
        }
377
378 3
        if (!$otherPerson->getNfgProfile()) {
379
            // The other person isn't linked with NFG, so $person can safely get the CPF
380 1
            $otherPerson->getPerson()->setCpf(null);
381 1
            $this->em->persist($otherPerson->getPerson());
382 1
            $this->em->flush($otherPerson->getPerson());
383
384 1
            $this->mailer->notifyCpfLost($otherPerson->getPerson());
385
386 1
            return;
387
        }
388
389
        // Both users are linked to the same NFG account
390
        // What should we do?
391 2
        if (false === $overrideExisting) {
392 1
            throw new NfgAccountCollisionException();
393
        }
394
        // The user's choice was to remove the previous connection and use this new one
395 1
        $otherPerson->getPerson()->setCpf(null);
396 1
        $this->em->persist($otherPerson->getPerson());
397 1
        $this->em->flush($otherPerson->getPerson());
398 1
        $this->disconnect($otherPerson);
399 1
        $this->mailer->notifyConnectionTransferred($otherPerson->getPerson());
400 1
    }
401
402
    /**
403
     * @param Request $request
404
     * @param PersonMeuRS $personMeuRS
405
     * @param NfgProfile $nfgProfile
406
     * @return null|RedirectResponse|Response
407
     * @throws OverrideResponseException
408
     */
409 7
    private function register(Request $request, PersonMeuRS $personMeuRS, NfgProfile $nfgProfile)
410
    {
411 7
        $email = $this->emailCanonicalizer->canonicalize($nfgProfile->getEmail());
412 7
        if ($this->meuRSHelper->getPersonByEmail($email, true) !== null) {
413 1
            throw new EmailInUseException();
414
        }
415
416 6
        $sanitizedCpf = $this->sanitizeCpf($nfgProfile->getCpf());
417 6
        $otherPersonMeuRS = $this->meuRSHelper->getPersonByCpf($sanitizedCpf, true);
418
419 6
        if ($otherPersonMeuRS !== null) {
420 3
            if ($otherPersonMeuRS->getNfgProfile()) {
421 1
                $otherPersonNfgCpf = $otherPersonMeuRS->getNfgProfile()->getCpf();
422
            } else {
423 2
                $otherPersonNfgCpf = null;
424
            }
425 3
            if ($otherPersonMeuRS->getNfgAccessToken() && $otherPersonNfgCpf == $sanitizedCpf) {
426 1
                $response = $this->logInUser($otherPersonMeuRS->getPerson());
427 1
                throw new OverrideResponseException($response);
428
            }
429 2
            $this->handleCpfCollision($otherPersonMeuRS);
430
        }
431
432 4
        $names = explode(' ', $nfgProfile->getName());
433
434
        /** @var PersonInterface $user */
435 4
        $user = $this->userManager->createUser();
436 4
        $user->setUsername(Uuid::uuid4()->toString());
437
        $user
438 4
            ->setFirstName(array_shift($names))
439 4
            ->setSurname(implode(' ', $names))
440 4
            ->setEmail($nfgProfile->getEmail())
441 4
            ->setCpf($sanitizedCpf)
442 4
            ->setBirthdate($nfgProfile->getBirthdate())
443 4
            ->setMobile($nfgProfile->getMobile())
444 4
            ->setPassword('')
445 4
            ->setEnabled(true);
446
447 4
        $event = new GetResponseUserEvent($user, $request);
448 4
        $this->dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);
449
450 4
        if (null !== $event->getResponse()) {
451 2
            throw new OverrideResponseException($event->getResponse());
452
        }
453
454 2
        $form = $this->formFactory->createForm();
455 2
        $form->setData($user);
456
457 2
        $event = new FormEvent($form, $request);
458 2
        $this->dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);
459
460 2
        $this->userManager->updateUser($user);
461
462 2
        $nfgProfile = $this->syncNfgProfile($nfgProfile);
463 2
        $personMeuRS->setPerson($user);
464 2
        $personMeuRS->setNfgProfile($nfgProfile);
465 2
        $this->em->persist($personMeuRS);
466 2
        $this->em->persist($nfgProfile);
467 2
        $this->em->flush();
468
469 2
        if (null === $response = $event->getResponse()) {
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $response is correct as $event->getResponse() targeting FOS\UserBundle\Event\FormEvent::getResponse() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
introduced by
The condition null === $response = $event->getResponse() is always true.
Loading history...
470 2
            $url = $this->router->generate('fos_user_registration_confirmed');
471 2
            $response = new RedirectResponse($url);
472
        }
473
474 2
        $this->dispatcher->dispatch(
475 2
            FOSUserEvents::REGISTRATION_COMPLETED,
476 2
            new FilterUserResponseEvent($user, $request, $response)
477
        );
478
479 2
        return $response;
480
    }
481
482
    /**
483
     * @param NfgProfile $latestNfgProfile
484
     * @return NfgProfile
485
     */
486 6
    public function syncNfgProfile(NfgProfile $latestNfgProfile)
487
    {
488 6
        $existingNfgProfile = $this->nfgProfileRepository->findByCpf($latestNfgProfile->getCpf());
489
490 6
        if ($existingNfgProfile instanceof NfgProfile) {
491
            $existingNfgProfile
492 1
                ->setName($latestNfgProfile->getName())
493 1
                ->setEmail($latestNfgProfile->getEmail())
494 1
                ->setBirthdate($latestNfgProfile->getBirthdate())
495 1
                ->setMobile($latestNfgProfile->getMobile())
496 1
                ->setAccessLvl($latestNfgProfile->getAccessLvl())
497 1
                ->setVoterRegistration($latestNfgProfile->getVoterRegistration())
498 1
                ->setVoterRegistrationSit($latestNfgProfile->getVoterRegistrationSit());
499
500 1
            return $existingNfgProfile;
501
        } else {
502 5
            return $latestNfgProfile;
503
        }
504
    }
505
506 3
    private function logInUser(PersonInterface $user, array $params = [])
507
    {
508 3
        $response = new RedirectResponse($this->router->generate('lc_home'));
509
510
        try {
511 3
            $this->loginManager->logInUser($this->firewallName, $user, $response);
512 1
        } catch (AccountStatusException $e) {
513
            // User account is disabled or something like that
514 1
            throw $e;
515
        }
516
517 2
        $event = new GetLoginCallbackResponseEvent($params, $response);
518 2
        $this->dispatcher->dispatch(NfgEvents::LOGIN_CALLBACK_RESPONSE, $event);
519
520 2
        return $event->getResponse();
521
    }
522
523 2
    private function handleCpfCollision(PersonMeuRS $otherPersonMeuRS)
524
    {
525 2
        if (!$otherPersonMeuRS->getNfgAccessToken()) {
526 1
            $otherPersonMeuRS->getPerson()->setCpf(null);
527 1
            $this->mailer->notifyCpfLost($otherPersonMeuRS->getPerson());
528 1
            $this->em->persist($otherPersonMeuRS->getPerson());
529 1
            $this->em->flush($otherPersonMeuRS->getPerson());
530
        } else {
531 1
            throw new CpfInUseException();
532
        }
533 1
    }
534
}
535