Completed
Push — scalar-types/user ( 04f6b1 )
by Kamil
23:44
created

UserController::handleChangePassword()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 17
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);
0 ignored issues
show
Documentation introduced by
$changePassword is of type object<Sylius\Bundle\Use...m\Model\ChangePassword>, but the function expects a object<Sylius\Component\...ResourceInterface>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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);
0 ignored issues
show
Documentation introduced by
$passwordReset is of type object<Sylius\Bundle\Use...rm\Model\PasswordReset>, but the function expects a object<Sylius\Component\...ResourceInterface>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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);
0 ignored issues
show
Documentation introduced by
$passwordReset is of type object<Sylius\Bundle\Use...l\PasswordResetRequest>, but the function expects a object<Sylius\Component\...ResourceInterface>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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
     * @param string $type
288
     * @param string $message
289
     */
290
    protected function addFlash(string $type, string $message): void
291
    {
292
        $translator = $this->container->get('translator');
293
        $this->container->get('session')->getFlashBag()->add($type, $translator->trans($message, [], 'flashes'));
294
    }
295
296
    /**
297
     * @param RequestConfiguration $configuration
298
     * @param string $type
299
     * @param ResourceInterface|null $resource
300
     *
301
     * @return FormInterface
302
     */
303
    protected function createResourceForm(
304
        RequestConfiguration $configuration,
305
        string $type,
306
        ?ResourceInterface $resource
307
    ): FormInterface {
308
        if (!$configuration->isHtmlRequest()) {
309
            return $this->container->get('form.factory')->createNamed('', $type, $resource, ['csrf_protection' => false]);
310
        }
311
312
        return $this->container->get('form.factory')->create($type, $resource);
313
    }
314
315
    /**
316
     * @param Request $request
317
     * @param RequestConfiguration $configuration
318
     * @param UserInterface $user
319
     *
320
     * @return Response
321
     */
322
    protected function handleExpiredToken(Request $request, RequestConfiguration $configuration, UserInterface $user): Response
323
    {
324
        $user->setPasswordResetToken(null);
325
        $user->setPasswordRequestedAt(null);
326
327
        $this->manager->flush();
328
329
        if (!$configuration->isHtmlRequest()) {
330
            return $this->viewHandler->handle($configuration, View::create($user, Response::HTTP_BAD_REQUEST));
331
        }
332
333
        $this->addFlash('error', 'sylius.user.expire_password_reset_token');
334
335
        $redirectRouteName = $this->getSyliusAttribute($request, 'redirect', null);
336
        Assert::notNull($redirectRouteName, 'Redirect is not configured.');
337
338
        return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
339
    }
340
341
    /**
342
     * @param GeneratorInterface $generator
343
     * @param UserInterface $user
344
     * @param string $senderEvent
345
     */
346
    protected function handleResetPasswordRequest(
347
        GeneratorInterface $generator,
348
        UserInterface $user,
349
        string $senderEvent
350
    ): void {
351
        $user->setPasswordResetToken($generator->generate());
352
        $user->setPasswordRequestedAt(new \DateTime());
353
354
        /* I have to use doctrine manager directly, because domain manager functions add a flash messages. I can't get rid of them.*/
355
        $manager = $this->container->get('doctrine.orm.default_entity_manager');
356
        $manager->persist($user);
357
        $manager->flush();
358
359
        $dispatcher = $this->container->get('event_dispatcher');
360
        $dispatcher->dispatch($senderEvent, new GenericEvent($user));
361
    }
362
363
    /**
364
     * @param Request $request
365
     * @param RequestConfiguration $configuration
366
     * @param UserInterface $user
367
     * @param string $newPassword
368
     *
369
     * @return Response
370
     */
371
    protected function handleResetPassword(
372
        Request $request,
373
        RequestConfiguration $configuration,
374
        UserInterface $user,
375
        string $newPassword
376
    ): Response {
377
        $user->setPlainPassword($newPassword);
378
        $user->setPasswordResetToken(null);
379
        $user->setPasswordRequestedAt(null);
380
381
        $dispatcher = $this->container->get('event_dispatcher');
382
        $dispatcher->dispatch(UserEvents::PRE_PASSWORD_RESET, new GenericEvent($user));
383
384
        $this->manager->flush();
385
        $this->addFlash('success', 'sylius.user.reset_password');
386
387
        $dispatcher->dispatch(UserEvents::POST_PASSWORD_RESET, new GenericEvent($user));
388
389
        if (!$configuration->isHtmlRequest()) {
390
            return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
391
        }
392
393
        $redirectRouteName = $this->getSyliusAttribute($request, 'redirect', null);
394
        Assert::notNull($redirectRouteName, 'Redirect is not configured.');
395
396
        return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
397
    }
398
399
    /**
400
     * @param Request $request
401
     * @param RequestConfiguration $configuration
402
     * @param UserInterface $user
403
     * @param string $newPassword
404
     *
405
     * @return Response
406
     */
407
    protected function handleChangePassword(
408
        Request $request,
409
        RequestConfiguration $configuration,
410
        UserInterface $user,
411
        string $newPassword
412
    ): Response {
413
        $user->setPlainPassword($newPassword);
414
415
        $dispatcher = $this->container->get('event_dispatcher');
416
        $dispatcher->dispatch(UserEvents::PRE_PASSWORD_CHANGE, new GenericEvent($user));
417
418
        $this->manager->flush();
419
        $this->addFlash('success', 'sylius.user.change_password');
420
421
        $dispatcher->dispatch(UserEvents::POST_PASSWORD_CHANGE, new GenericEvent($user));
422
423
        if (!$configuration->isHtmlRequest()) {
424
            return $this->viewHandler->handle($configuration, View::create(null, Response::HTTP_NO_CONTENT));
425
        }
426
427
        $redirectRouteName = $this->getSyliusAttribute($request, 'redirect', null);
428
        Assert::notNull($redirectRouteName, 'Redirect is not configured.');
429
430
        return new RedirectResponse($this->container->get('router')->generate($redirectRouteName));
431
    }
432
433
    /**
434
     * @param Request $request
435
     * @param string $attribute
436
     * @param mixed $default
437
     *
438
     * @return mixed
439
     */
440
    private function getSyliusAttribute(Request $request, string $attribute, $default = null)
441
    {
442
        $attributes = $request->attributes->get('_sylius');
443
444
        return $attributes[$attribute] ?? $default;
445
    }
446
}
447