Completed
Push — master ( 6570ae...141d1f )
by Kamil
25:19 queued 17s
created

UserController   F

Complexity

Total Complexity 45

Size/Duplication

Total Lines 431
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 24

Importance

Changes 0
Metric Value
wmc 45
lcom 1
cbo 24
dl 0
loc 431
rs 3.9596
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
B changePasswordAction() 0 27 5
A requestPasswordResetTokenAction() 0 7 1
A requestPasswordResetPinAction() 0 7 1
B resetPasswordAction() 0 35 6
B verifyAction() 0 39 4
B requestVerificationTokenAction() 0 42 6
C prepareResetPasswordRequest() 0 48 8
A addFlash() 0 5 1
A createResourceForm() 0 11 2
A handleExpiredToken() 0 18 2
A handleResetPasswordRequest() 0 16 1
B handleResetPassword() 0 27 2
B handleChangePassword() 0 25 2
A getUser() 0 14 3
A getSyliusAttribute() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like UserController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UserController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Bundle\UserBundle\Controller;
15
16
use FOS\RestBundle\View\View;
17
use Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration;
18
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
19
use Sylius\Bundle\UserBundle\Form\Model\ChangePassword;
20
use Sylius\Bundle\UserBundle\Form\Model\PasswordReset;
21
use Sylius\Bundle\UserBundle\Form\Model\PasswordResetRequest;
22
use Sylius\Bundle\UserBundle\Form\Type\UserChangePasswordType;
23
use Sylius\Bundle\UserBundle\Form\Type\UserRequestPasswordResetType;
24
use Sylius\Bundle\UserBundle\Form\Type\UserResetPasswordType;
25
use Sylius\Bundle\UserBundle\UserEvents;
26
use Sylius\Component\User\Model\UserInterface;
27
use Sylius\Component\User\Security\Generator\GeneratorInterface;
28
use Symfony\Component\EventDispatcher\GenericEvent;
29
use Symfony\Component\Form\FormInterface;
30
use Symfony\Component\HttpFoundation\RedirectResponse;
31
use Symfony\Component\HttpFoundation\Request;
32
use Symfony\Component\HttpFoundation\Response;
33
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
34
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
35
use Webmozart\Assert\Assert;
36
37
/**
38
 * @author Łukasz Chruściel <[email protected]>
39
 * @author Jan Góralski <[email protected]>
40
 */
41
class UserController extends ResourceController
42
{
43
    /**
44
     * @param Request $request
45
     *
46
     * @return Response
47
     */
48
    public function changePasswordAction(Request $request): Response
49
    {
50
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
51
52
        if (!$this->container->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
53
            throw new AccessDeniedException('You have to be registered user to access this section.');
54
        }
55
56
        $user = $this->container->get('security.token_storage')->getToken()->getUser();
57
58
        $changePassword = new ChangePassword();
59
        $formType = $this->getSyliusAttribute($request, 'form', UserChangePasswordType::class);
60
        $form = $this->createResourceForm($configuration, $formType, $changePassword);
61
62
        if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->handleRequest($request)->isValid()) {
63
            return $this->handleChangePassword($request, $configuration, $user, $changePassword->getNewPassword());
64
        }
65
66
        if (!$configuration->isHtmlRequest()) {
67
            return $this->viewHandler->handle($configuration, View::create($form, Response::HTTP_BAD_REQUEST));
68
        }
69
70
        return $this->container->get('templating')->renderResponse(
71
            $configuration->getTemplate('changePassword.html'),
72
            ['form' => $form->createView()]
73
        );
74
    }
75
76
    /**
77
     * @param Request $request
78
     *
79
     * @return Response
80
     */
81
    public function requestPasswordResetTokenAction(Request $request): Response
82
    {
83
        /** @var GeneratorInterface $generator */
84
        $generator = $this->container->get(sprintf('sylius.%s.token_generator.password_reset', $this->metadata->getName()));
85
86
        return $this->prepareResetPasswordRequest($request, $generator, UserEvents::REQUEST_RESET_PASSWORD_TOKEN);
87
    }
88
89
    /**
90
     * @param Request $request
91
     *
92
     * @return Response
93
     */
94
    public function requestPasswordResetPinAction(Request $request): Response
95
    {
96
        /** @var GeneratorInterface $generator */
97
        $generator = $this->container->get(sprintf('sylius.%s.pin_generator.password_reset', $this->metadata->getName()));
98
99
        return $this->prepareResetPasswordRequest($request, $generator, UserEvents::REQUEST_RESET_PASSWORD_PIN);
100
    }
101
102
    /**
103
     * @param Request $request
104
     * @param string $token
105
     *
106
     * @return Response
107
     */
108
    public function resetPasswordAction(Request $request, string $token): Response
109
    {
110
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
111
        /** @var UserInterface $user */
112
        $user = $this->repository->findOneBy(['passwordResetToken' => $token]);
113
        if (null === $user) {
114
            throw new NotFoundHttpException('Token not found.');
115
        }
116
117
        $resetting = $this->metadata->getParameter('resetting');
118
        $lifetime = new \DateInterval($resetting['token']['ttl']);
119
        if (!$user->isPasswordRequestNonExpired($lifetime)) {
120
            return $this->handleExpiredToken($request, $configuration, $user);
121
        }
122
123
        $passwordReset = new PasswordReset();
124
        $formType = $this->getSyliusAttribute($request, 'form', UserResetPasswordType::class);
125
        $form = $this->createResourceForm($configuration, $formType, $passwordReset);
126
127
        if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->handleRequest($request)->isValid()) {
128
            return $this->handleResetPassword($request, $configuration, $user, $passwordReset->getPassword());
129
        }
130
131
        if (!$configuration->isHtmlRequest()) {
132
            return $this->viewHandler->handle($configuration, View::create($form, Response::HTTP_BAD_REQUEST));
133
        }
134
135
        return $this->container->get('templating')->renderResponse(
136
            $configuration->getTemplate('resetPassword.html'),
137
            [
138
                'form' => $form->createView(),
139
                'user' => $user,
140
            ]
141
        );
142
    }
143
144
    /**
145
     * @param Request $request
146
     * @param string $token
147
     *
148
     * @return Response
149
     */
150
    public function verifyAction(Request $request, string $token): Response
151
    {
152
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
153
        $redirectRoute = $this->getSyliusAttribute($request, 'redirect', null);
154
155
        $response = $this->redirectToRoute($redirectRoute);
156
157
        /** @var UserInterface $user */
158
        $user = $this->repository->findOneBy(['emailVerificationToken' => $token]);
159
        if (null === $user) {
160
            if (!$configuration->isHtmlRequest()) {
161
                return $this->viewHandler->handle($configuration, View::create($configuration, Response::HTTP_BAD_REQUEST));
162
            }
163
164
            $this->addFlash('error', 'sylius.user.verify_email_by_invalid_token');
165
166
            return $this->redirectToRoute($redirectRoute);
167
        }
168
169
        $user->setVerifiedAt(new \DateTime());
170
        $user->setEmailVerificationToken(null);
171
        $user->enable();
172
173
        $eventDispatcher = $this->container->get('event_dispatcher');
174
        $eventDispatcher->dispatch(UserEvents::PRE_EMAIL_VERIFICATION, new GenericEvent($user));
175
176
        $this->manager->flush();
177
178
        $eventDispatcher->dispatch(UserEvents::POST_EMAIL_VERIFICATION, new GenericEvent($user));
179
180
        if (!$configuration->isHtmlRequest()) {
181
            return $this->viewHandler->handle($configuration, View::create($user));
182
        }
183
184
        $flashMessage = $this->getSyliusAttribute($request, 'flash', 'sylius.user.verify_email');
185
        $this->addFlash('success', $flashMessage);
186
187
        return $response;
188
    }
189
190
    /**
191
     * @param Request $request
192
     *
193
     * @return Response
194
     */
195
    public function requestVerificationTokenAction(Request $request): Response
196
    {
197
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
198
        $redirectRoute = $this->getSyliusAttribute($request, 'redirect', 'referer');
199
200
        $user = $this->getUser();
201
        if (null === $user) {
202
            if (!$configuration->isHtmlRequest()) {
203
                return $this->viewHandler->handle($configuration, View::create($configuration, Response::HTTP_UNAUTHORIZED));
204
            }
205
206
            $this->addFlash('notice', 'sylius.user.verify_no_user');
207
208
            return $this->redirectHandler->redirectToRoute($configuration, $redirectRoute);
209
        }
210
211
        if (null !== $user->getVerifiedAt()) {
212
            if (!$configuration->isHtmlRequest()) {
213
                return $this->viewHandler->handle($configuration, View::create($configuration, Response::HTTP_BAD_REQUEST));
214
            }
215
216
            $this->addFlash('notice', 'sylius.user.verify_verified_email');
217
218
            return $this->redirectHandler->redirectToRoute($configuration, $redirectRoute);
219
        }
220
221
        $tokenGenerator = $this->container->get(sprintf('sylius.%s.token_generator.email_verification', $this->metadata->getName()));
222
        $user->setEmailVerificationToken($tokenGenerator->generate());
223
224
        $this->manager->flush();
225
226
        $eventDispatcher = $this->container->get('event_dispatcher');
227
        $eventDispatcher->dispatch(UserEvents::REQUEST_VERIFICATION_TOKEN, new GenericEvent($user));
228
229
        if (!$configuration->isHtmlRequest()) {
230
            return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
231
        }
232
233
        $this->addFlash('success', 'sylius.user.verify_email_request');
234
235
        return $this->redirectHandler->redirectToRoute($configuration, $redirectRoute);
236
    }
237
238
    /**
239
     * @param Request $request
240
     * @param GeneratorInterface $generator
241
     * @param string $senderEvent
242
     *
243
     * @return Response
244
     */
245
    protected function prepareResetPasswordRequest(Request $request, GeneratorInterface $generator, string $senderEvent): Response
246
    {
247
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
248
249
        $passwordReset = new PasswordResetRequest();
250
        $formType = $this->getSyliusAttribute($request, 'form', UserRequestPasswordResetType::class);
251
        $form = $this->createResourceForm($configuration, $formType, $passwordReset);
252
        $template = $this->getSyliusAttribute($request, 'template', null);
253
        if ($configuration->isHtmlRequest()) {
254
            Assert::notNull($template, 'Template is not configured.');
255
        }
256
257
        if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->handleRequest($request)->isValid()) {
258
            $user = $this->repository->findOneByEmail($passwordReset->getEmail());
0 ignored issues
show
Bug introduced by
The method findOneByEmail() does not exist on Sylius\Component\Resourc...ory\RepositoryInterface. Did you maybe mean findOneBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
259
            if (null !== $user) {
260
                $this->handleResetPasswordRequest($generator, $user, $senderEvent);
261
            }
262
263
            if (!$configuration->isHtmlRequest()) {
264
                return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
265
            }
266
267
            $this->addFlash('success', 'sylius.user.reset_password_request');
268
            $redirectRoute = $this->getSyliusAttribute($request, 'redirect', null);
269
            Assert::notNull($redirectRoute, 'Redirect is not configured.');
270
271
            if (is_array($redirectRoute)) {
272
                return $this->redirectHandler->redirectToRoute(
273
                    $configuration,
274
                    $configuration->getParameters()->get('redirect')['route'],
275
                    $configuration->getParameters()->get('redirect')['parameters']
276
                );
277
            }
278
279
            return $this->redirectHandler->redirectToRoute($configuration, $redirectRoute);
280
        }
281
282
        if (!$configuration->isHtmlRequest()) {
283
            return $this->viewHandler->handle($configuration, View::create($form, Response::HTTP_BAD_REQUEST));
284
        }
285
286
        return $this->container->get('templating')->renderResponse(
287
            $template,
288
            [
289
                'form' => $form->createView(),
290
            ]
291
        );
292
    }
293
294
    /**
295
     * {@inheritdoc}
296
     */
297
    protected function addFlash($type, $message): void
298
    {
299
        $translator = $this->container->get('translator');
300
        $this->container->get('session')->getFlashBag()->add($type, $translator->trans($message, [], 'flashes'));
301
    }
302
303
    /**
304
     * @param RequestConfiguration $configuration
305
     * @param string $type
306
     * @param object $object
307
     *
308
     * @return FormInterface
309
     */
310
    protected function createResourceForm(
311
        RequestConfiguration $configuration,
312
        string $type,
313
        $object
314
    ): FormInterface {
315
        if (!$configuration->isHtmlRequest()) {
316
            return $this->container->get('form.factory')->createNamed('', $type, $object, ['csrf_protection' => false]);
317
        }
318
319
        return $this->container->get('form.factory')->create($type, $object);
320
    }
321
322
    /**
323
     * @param Request $request
324
     * @param RequestConfiguration $configuration
325
     * @param UserInterface $user
326
     *
327
     * @return Response
328
     */
329
    protected function handleExpiredToken(Request $request, RequestConfiguration $configuration, UserInterface $user): Response
330
    {
331
        $user->setPasswordResetToken(null);
332
        $user->setPasswordRequestedAt(null);
333
334
        $this->manager->flush();
335
336
        if (!$configuration->isHtmlRequest()) {
337
            return $this->viewHandler->handle($configuration, View::create($user, Response::HTTP_BAD_REQUEST));
338
        }
339
340
        $this->addFlash('error', 'sylius.user.expire_password_reset_token');
341
342
        $redirectRouteName = $this->getSyliusAttribute($request, 'redirect', null);
343
        Assert::notNull($redirectRouteName, 'Redirect is not configured.');
344
345
        return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
346
    }
347
348
    /**
349
     * @param GeneratorInterface $generator
350
     * @param UserInterface $user
351
     * @param string $senderEvent
352
     */
353
    protected function handleResetPasswordRequest(
354
        GeneratorInterface $generator,
355
        UserInterface $user,
356
        string $senderEvent
357
    ): void {
358
        $user->setPasswordResetToken($generator->generate());
359
        $user->setPasswordRequestedAt(new \DateTime());
360
361
        // I have to use doctrine manager directly, because domain manager functions add a flash messages. I can't get rid of them.
362
        $manager = $this->container->get('doctrine.orm.default_entity_manager');
363
        $manager->persist($user);
364
        $manager->flush();
365
366
        $dispatcher = $this->container->get('event_dispatcher');
367
        $dispatcher->dispatch($senderEvent, new GenericEvent($user));
368
    }
369
370
    /**
371
     * @param Request $request
372
     * @param RequestConfiguration $configuration
373
     * @param UserInterface $user
374
     * @param string $newPassword
375
     *
376
     * @return Response
377
     */
378
    protected function handleResetPassword(
379
        Request $request,
380
        RequestConfiguration $configuration,
381
        UserInterface $user,
382
        string $newPassword
383
    ): Response {
384
        $user->setPlainPassword($newPassword);
385
        $user->setPasswordResetToken(null);
386
        $user->setPasswordRequestedAt(null);
387
388
        $dispatcher = $this->container->get('event_dispatcher');
389
        $dispatcher->dispatch(UserEvents::PRE_PASSWORD_RESET, new GenericEvent($user));
390
391
        $this->manager->flush();
392
        $this->addFlash('success', 'sylius.user.reset_password');
393
394
        $dispatcher->dispatch(UserEvents::POST_PASSWORD_RESET, new GenericEvent($user));
395
396
        if (!$configuration->isHtmlRequest()) {
397
            return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
398
        }
399
400
        $redirectRouteName = $this->getSyliusAttribute($request, 'redirect', null);
401
        Assert::notNull($redirectRouteName, 'Redirect is not configured.');
402
403
        return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
404
    }
405
406
    /**
407
     * @param Request $request
408
     * @param RequestConfiguration $configuration
409
     * @param UserInterface $user
410
     * @param string $newPassword
411
     *
412
     * @return Response
413
     */
414
    protected function handleChangePassword(
415
        Request $request,
416
        RequestConfiguration $configuration,
417
        UserInterface $user,
418
        string $newPassword
419
    ): Response {
420
        $user->setPlainPassword($newPassword);
421
422
        $dispatcher = $this->container->get('event_dispatcher');
423
        $dispatcher->dispatch(UserEvents::PRE_PASSWORD_CHANGE, new GenericEvent($user));
424
425
        $this->manager->flush();
426
        $this->addFlash('success', 'sylius.user.change_password');
427
428
        $dispatcher->dispatch(UserEvents::POST_PASSWORD_CHANGE, new GenericEvent($user));
429
430
        if (!$configuration->isHtmlRequest()) {
431
            return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
432
        }
433
434
        $redirectRouteName = $this->getSyliusAttribute($request, 'redirect', null);
435
        Assert::notNull($redirectRouteName, 'Redirect is not configured.');
436
437
        return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
438
    }
439
440
    /**
441
     * @return UserInterface|null
442
     */
443
    protected function getUser(): ?UserInterface
444
    {
445
        $user = parent::getUser();
446
        $authorizationChecker = $this->container->get('security.authorization_checker');
447
448
        if (
449
            $authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED') &&
450
            $user instanceof UserInterface
451
        ) {
452
            return $user;
453
        }
454
455
        return null;
456
    }
457
458
    /**
459
     * @param Request $request
460
     * @param string $attribute
461
     * @param mixed $default
462
     *
463
     * @return mixed
464
     */
465
    private function getSyliusAttribute(Request $request, string $attribute, $default = null)
466
    {
467
        $attributes = $request->attributes->get('_sylius');
468
469
        return $attributes[$attribute] ?? $default;
470
    }
471
}
472