Completed
Push — scalar-types/order ( bd3d7c )
by Kamil
21:55
created

UserController::handleResetPassword()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 19
nc 2
nop 4
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\Resource\Model\ResourceInterface;
27
use Sylius\Component\User\Model\UserInterface;
28
use Sylius\Component\User\Security\Generator\GeneratorInterface;
29
use Symfony\Component\EventDispatcher\GenericEvent;
30
use Symfony\Component\Form\FormInterface;
31
use Symfony\Component\HttpFoundation\RedirectResponse;
32
use Symfony\Component\HttpFoundation\Request;
33
use Symfony\Component\HttpFoundation\Response;
34
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
35
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
36
use Webmozart\Assert\Assert;
37
38
/**
39
 * @author Łukasz Chruściel <[email protected]>
40
 * @author Jan Góralski <[email protected]>
41
 */
42
class UserController extends ResourceController
43
{
44
    /**
45
     * @param Request $request
46
     *
47
     * @return Response
48
     */
49
    public function changePasswordAction(Request $request): Response
50
    {
51
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
52
53
        if (!$this->container->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
54
            throw new AccessDeniedException('You have to be registered user to access this section.');
55
        }
56
57
        $user = $this->container->get('security.token_storage')->getToken()->getUser();
58
59
        $changePassword = new ChangePassword();
60
        $formType = $this->getSyliusAttribute($request, 'form', UserChangePasswordType::class);
61
        $form = $this->createResourceForm($configuration, $formType, $changePassword);
62
63
        if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->handleRequest($request)->isValid()) {
64
            return $this->handleChangePassword($request, $configuration, $user, $changePassword->getNewPassword());
65
        }
66
67
        if (!$configuration->isHtmlRequest()) {
68
            return $this->viewHandler->handle($configuration, View::create($form, Response::HTTP_BAD_REQUEST));
69
        }
70
71
        return $this->container->get('templating')->renderResponse(
72
            $configuration->getTemplate('changePassword.html'),
73
            ['form' => $form->createView()]
74
        );
75
    }
76
77
    /**
78
     * @param Request $request
79
     *
80
     * @return Response
81
     */
82
    public function requestPasswordResetTokenAction(Request $request): Response
83
    {
84
        /** @var GeneratorInterface $generator */
85
        $generator = $this->container->get(sprintf('sylius.%s.token_generator.password_reset', $this->metadata->getName()));
86
87
        return $this->prepareResetPasswordRequest($request, $generator, UserEvents::REQUEST_RESET_PASSWORD_TOKEN);
88
    }
89
90
    /**
91
     * @param Request $request
92
     *
93
     * @return Response
94
     */
95
    public function requestPasswordResetPinAction(Request $request): Response
96
    {
97
        /** @var GeneratorInterface $generator */
98
        $generator = $this->container->get(sprintf('sylius.%s.pin_generator.password_reset', $this->metadata->getName()));
99
100
        return $this->prepareResetPasswordRequest($request, $generator, UserEvents::REQUEST_RESET_PASSWORD_PIN);
101
    }
102
103
    /**
104
     * @param Request $request
105
     * @param string $token
106
     *
107
     * @return Response
108
     */
109
    public function resetPasswordAction(Request $request, string $token): Response
110
    {
111
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
112
        /** @var UserInterface $user */
113
        $user = $this->repository->findOneBy(['passwordResetToken' => $token]);
114
        if (null === $user) {
115
            throw new NotFoundHttpException('Token not found.');
116
        }
117
118
        $resetting = $this->metadata->getParameter('resetting');
119
        $lifetime = new \DateInterval($resetting['token']['ttl']);
120
        if (!$user->isPasswordRequestNonExpired($lifetime)) {
121
            return $this->handleExpiredToken($request, $configuration, $user);
122
        }
123
124
        $passwordReset = new PasswordReset();
125
        $formType = $this->getSyliusAttribute($request, 'form', UserResetPasswordType::class);
126
        $form = $this->createResourceForm($configuration, $formType, $passwordReset);
127
128
        if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->handleRequest($request)->isValid()) {
129
            return $this->handleResetPassword($request, $configuration, $user, $passwordReset->getPassword());
130
        }
131
132
        if (!$configuration->isHtmlRequest()) {
133
            return $this->viewHandler->handle($configuration, View::create($form, Response::HTTP_BAD_REQUEST));
134
        }
135
136
        return $this->container->get('templating')->renderResponse(
137
            $configuration->getTemplate('resetPassword.html'),
138
            [
139
                'form' => $form->createView(),
140
                'user' => $user,
141
            ]
142
        );
143
    }
144
145
    /**
146
     * @param Request $request
147
     * @param string $token
148
     *
149
     * @return Response
150
     */
151
    public function verifyAction(Request $request, string $token): Response
152
    {
153
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
154
        $redirectRoute = $this->getSyliusAttribute($request, 'redirect', null);
155
156
        $response = $this->redirectToRoute($redirectRoute);
157
158
        /** @var UserInterface $user */
159
        $user = $this->repository->findOneBy(['emailVerificationToken' => $token]);
160
        if (null === $user) {
161
            if (!$configuration->isHtmlRequest()) {
162
                return $this->viewHandler->handle($configuration, View::create($configuration, Response::HTTP_BAD_REQUEST));
163
            }
164
165
            $this->addFlash('error', 'sylius.user.verify_email_by_invalid_token');
166
167
            return $this->redirectToRoute($redirectRoute);
168
        }
169
170
        $user->setVerifiedAt(new \DateTime());
171
        $user->setEmailVerificationToken(null);
172
        $user->enable();
173
174
        $eventDispatcher = $this->container->get('event_dispatcher');
175
        $eventDispatcher->dispatch(UserEvents::PRE_EMAIL_VERIFICATION, new GenericEvent($user));
176
177
        $this->manager->flush();
178
179
        $eventDispatcher->dispatch(UserEvents::POST_EMAIL_VERIFICATION, new GenericEvent($user));
180
181
        if (!$configuration->isHtmlRequest()) {
182
            return $this->viewHandler->handle($configuration, View::create($user));
183
        }
184
185
        $flashMessage = $this->getSyliusAttribute($request, 'flash', 'sylius.user.verify_email');
186
        $this->addFlash('success', $flashMessage);
187
188
        return $response;
189
    }
190
191
    /**
192
     * @param Request $request
193
     *
194
     * @return Response
195
     */
196
    public function requestVerificationTokenAction(Request $request): Response
197
    {
198
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
199
        $redirectRoute = $this->getSyliusAttribute($request, 'redirect', 'referer');
200
201
        /** @var UserInterface $user */
202
        $user = $this->container->get('sylius.context.customer')->getCustomer()->getUser();
203
        if (null !== $user->getVerifiedAt()) {
204
            if (!$configuration->isHtmlRequest()) {
205
                return $this->viewHandler->handle($configuration, View::create($configuration, Response::HTTP_BAD_REQUEST));
206
            }
207
208
            $this->addFlash('notice', 'sylius.user.verify_verified_email');
209
210
            return $this->redirectHandler->redirectToRoute($configuration, $redirectRoute);
211
        }
212
213
        $tokenGenerator = $this->container->get(sprintf('sylius.%s.token_generator.email_verification', $this->metadata->getName()));
214
        $user->setEmailVerificationToken($tokenGenerator->generate());
215
216
        $this->manager->flush();
217
218
        $eventDispatcher = $this->container->get('event_dispatcher');
219
        $eventDispatcher->dispatch(UserEvents::REQUEST_VERIFICATION_TOKEN, new GenericEvent($user));
220
221
        if (!$configuration->isHtmlRequest()) {
222
            return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
223
        }
224
225
        $this->addFlash('success', 'sylius.user.verify_email_request');
226
227
        return $this->redirectHandler->redirectToRoute($configuration, $redirectRoute);
228
    }
229
230
    /**
231
     * @param Request $request
232
     * @param GeneratorInterface $generator
233
     * @param string $senderEvent
234
     *
235
     * @return Response
236
     */
237
    protected function prepareResetPasswordRequest(Request $request, GeneratorInterface $generator, string $senderEvent): Response
238
    {
239
        $configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
240
241
        $passwordReset = new PasswordResetRequest();
242
        $formType = $this->getSyliusAttribute($request, 'form', UserRequestPasswordResetType::class);
243
        $form = $this->createResourceForm($configuration, $formType, $passwordReset);
244
        $template = $this->getSyliusAttribute($request, 'template', null);
245
        if ($configuration->isHtmlRequest()) {
246
            Assert::notNull($template, 'Template is not configured.');
247
        }
248
249
        if (in_array($request->getMethod(), ['POST', 'PUT', 'PATCH'], true) && $form->handleRequest($request)->isValid()) {
250
            $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...
251
            if (null !== $user) {
252
                $this->handleResetPasswordRequest($generator, $user, $senderEvent);
253
            }
254
255
            if (!$configuration->isHtmlRequest()) {
256
                return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
257
            }
258
259
            $this->addFlash('success', 'sylius.user.reset_password_request');
260
            $redirectRoute = $this->getSyliusAttribute($request, 'redirect', null);
261
            Assert::notNull($redirectRoute, 'Redirect is not configured.');
262
263
            if (is_array($redirectRoute)) {
264
                return $this->redirectHandler->redirectToRoute(
265
                    $configuration,
266
                    $configuration->getParameters()->get('redirect')['route'],
267
                    $configuration->getParameters()->get('redirect')['parameters']
268
                );
269
            }
270
271
            return $this->redirectHandler->redirectToRoute($configuration, $redirectRoute);
272
        }
273
274
        if (!$configuration->isHtmlRequest()) {
275
            return $this->viewHandler->handle($configuration, View::create($form, Response::HTTP_BAD_REQUEST));
276
        }
277
278
        return $this->container->get('templating')->renderResponse(
279
            $template,
280
            [
281
                'form' => $form->createView(),
282
            ]
283
        );
284
    }
285
286
    /**
287
     * {@inheritdoc}
288
     */
289
    protected function addFlash($type, $message): void
290
    {
291
        $translator = $this->container->get('translator');
292
        $this->container->get('session')->getFlashBag()->add($type, $translator->trans($message, [], 'flashes'));
293
    }
294
295
    /**
296
     * @param RequestConfiguration $configuration
297
     * @param string $type
298
     * @param object $object
299
     *
300
     * @return FormInterface
301
     */
302
    protected function createResourceForm(
303
        RequestConfiguration $configuration,
304
        string $type,
305
        $object
306
    ): FormInterface {
307
        if (!$configuration->isHtmlRequest()) {
308
            return $this->container->get('form.factory')->createNamed('', $type, $object, ['csrf_protection' => false]);
309
        }
310
311
        return $this->container->get('form.factory')->create($type, $object);
312
    }
313
314
    /**
315
     * @param Request $request
316
     * @param RequestConfiguration $configuration
317
     * @param UserInterface $user
318
     *
319
     * @return Response
320
     */
321
    protected function handleExpiredToken(Request $request, RequestConfiguration $configuration, UserInterface $user): Response
322
    {
323
        $user->setPasswordResetToken(null);
324
        $user->setPasswordRequestedAt(null);
325
326
        $this->manager->flush();
327
328
        if (!$configuration->isHtmlRequest()) {
329
            return $this->viewHandler->handle($configuration, View::create($user, Response::HTTP_BAD_REQUEST));
330
        }
331
332
        $this->addFlash('error', 'sylius.user.expire_password_reset_token');
333
334
        $redirectRouteName = $this->getSyliusAttribute($request, 'redirect', null);
335
        Assert::notNull($redirectRouteName, 'Redirect is not configured.');
336
337
        return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
338
    }
339
340
    /**
341
     * @param GeneratorInterface $generator
342
     * @param UserInterface $user
343
     * @param string $senderEvent
344
     */
345
    protected function handleResetPasswordRequest(
346
        GeneratorInterface $generator,
347
        UserInterface $user,
348
        string $senderEvent
349
    ): void {
350
        $user->setPasswordResetToken($generator->generate());
351
        $user->setPasswordRequestedAt(new \DateTime());
352
353
        /* I have to use doctrine manager directly, because domain manager functions add a flash messages. I can't get rid of them.*/
354
        $manager = $this->container->get('doctrine.orm.default_entity_manager');
355
        $manager->persist($user);
356
        $manager->flush();
357
358
        $dispatcher = $this->container->get('event_dispatcher');
359
        $dispatcher->dispatch($senderEvent, new GenericEvent($user));
360
    }
361
362
    /**
363
     * @param Request $request
364
     * @param RequestConfiguration $configuration
365
     * @param UserInterface $user
366
     * @param string $newPassword
367
     *
368
     * @return Response
369
     */
370
    protected function handleResetPassword(
371
        Request $request,
372
        RequestConfiguration $configuration,
373
        UserInterface $user,
374
        string $newPassword
375
    ): Response {
376
        $user->setPlainPassword($newPassword);
377
        $user->setPasswordResetToken(null);
378
        $user->setPasswordRequestedAt(null);
379
380
        $dispatcher = $this->container->get('event_dispatcher');
381
        $dispatcher->dispatch(UserEvents::PRE_PASSWORD_RESET, new GenericEvent($user));
382
383
        $this->manager->flush();
384
        $this->addFlash('success', 'sylius.user.reset_password');
385
386
        $dispatcher->dispatch(UserEvents::POST_PASSWORD_RESET, new GenericEvent($user));
387
388
        if (!$configuration->isHtmlRequest()) {
389
            return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
390
        }
391
392
        $redirectRouteName = $this->getSyliusAttribute($request, 'redirect', null);
393
        Assert::notNull($redirectRouteName, 'Redirect is not configured.');
394
395
        return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
396
    }
397
398
    /**
399
     * @param Request $request
400
     * @param RequestConfiguration $configuration
401
     * @param UserInterface $user
402
     * @param string $newPassword
403
     *
404
     * @return Response
405
     */
406
    protected function handleChangePassword(
407
        Request $request,
408
        RequestConfiguration $configuration,
409
        UserInterface $user,
410
        string $newPassword
411
    ): Response {
412
        $user->setPlainPassword($newPassword);
413
414
        $dispatcher = $this->container->get('event_dispatcher');
415
        $dispatcher->dispatch(UserEvents::PRE_PASSWORD_CHANGE, new GenericEvent($user));
416
417
        $this->manager->flush();
418
        $this->addFlash('success', 'sylius.user.change_password');
419
420
        $dispatcher->dispatch(UserEvents::POST_PASSWORD_CHANGE, new GenericEvent($user));
421
422
        if (!$configuration->isHtmlRequest()) {
423
            return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
424
        }
425
426
        $redirectRouteName = $this->getSyliusAttribute($request, 'redirect', null);
427
        Assert::notNull($redirectRouteName, 'Redirect is not configured.');
428
429
        return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
430
    }
431
432
    /**
433
     * @param Request $request
434
     * @param string $attribute
435
     * @param mixed $default
436
     *
437
     * @return mixed
438
     */
439
    private function getSyliusAttribute(Request $request, string $attribute, $default = null)
440
    {
441
        $attributes = $request->attributes->get('_sylius');
442
443
        return $attributes[$attribute] ?? $default;
444
    }
445
}
446