Completed
Push — master ( cc7cf3...f7d42d )
by Kamil
19s
created

UserController::addTranslatedFlash()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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