Passed
Push — master ( 9b48f0...ece4a5 )
by Jan
04:51
created

ResetPasswordController   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 61
c 1
b 0
f 0
dl 0
loc 153
rs 10
wmc 15

5 Methods

Rating   Name   Duplication   Size   Complexity  
A request() 0 14 3
A processSendingPasswordResetEmail() 0 43 3
A __construct() 0 3 1
B reset() 0 51 6
A checkEmail() 0 10 2
1
<?php
2
3
namespace App\Controller;
4
5
use App\Entity\User;
6
use App\Form\ChangePasswordFormType;
7
use App\Form\ResetPasswordRequestFormType;
8
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
9
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
10
use Symfony\Component\HttpFoundation\RedirectResponse;
11
use Symfony\Component\HttpFoundation\Request;
12
use Symfony\Component\HttpFoundation\Response;
13
use Symfony\Component\Mailer\MailerInterface;
14
use Symfony\Component\Mime\Address;
15
use Symfony\Component\Routing\Annotation\Route;
16
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
17
use SymfonyCasts\Bundle\ResetPassword\Controller\ResetPasswordControllerTrait;
18
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
19
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;
20
21
/**
22
 * @Route("/reset-password")
23
 */
24
class ResetPasswordController extends AbstractController
25
{
26
    use ResetPasswordControllerTrait;
27
28
    private $resetPasswordHelper;
29
30
    public function __construct(ResetPasswordHelperInterface $resetPasswordHelper)
31
    {
32
        $this->resetPasswordHelper = $resetPasswordHelper;
33
    }
34
35
    /**
36
     * Display & process form to request a password reset.
37
     *
38
     * @Route("", name="app_forgot_password_request")
39
     */
40
    public function request(Request $request, MailerInterface $mailer): Response
41
    {
42
        $form = $this->createForm(ResetPasswordRequestFormType::class);
43
        $form->handleRequest($request);
44
45
        if ($form->isSubmitted() && $form->isValid()) {
46
            return $this->processSendingPasswordResetEmail(
47
                $form->get('email')->getData(),
48
                $mailer
49
            );
50
        }
51
52
        return $this->render('reset_password/request.html.twig', [
53
            'requestForm' => $form->createView(),
54
        ]);
55
    }
56
57
    /**
58
     * Confirmation page after a user has requested a password reset.
59
     *
60
     * @Route("/check-email", name="app_check_email")
61
     */
62
    public function checkEmail(): Response
63
    {
64
        // Generate a fake token if the user does not exist or someone hit this page directly.
65
        // This prevents exposing whether or not a user was found with the given email address or not
66
        if (null === ($resetToken = $this->getTokenObjectFromSession())) {
67
            $resetToken = $this->resetPasswordHelper->generateFakeResetToken();
0 ignored issues
show
Bug introduced by
The method generateFakeResetToken() does not exist on SymfonyCasts\Bundle\Rese...PasswordHelperInterface. Did you maybe mean generateResetToken()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

67
            /** @scrutinizer ignore-call */ 
68
            $resetToken = $this->resetPasswordHelper->generateFakeResetToken();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
68
        }
69
70
        return $this->render('reset_password/check_email.html.twig', [
71
            'resetToken' => $resetToken,
72
        ]);
73
    }
74
75
    /**
76
     * Validates and process the reset URL that the user clicked in their email.
77
     *
78
     * @Route("/reset/{token}", name="app_reset_password")
79
     */
80
    public function reset(Request $request, UserPasswordEncoderInterface $passwordEncoder, string $token = null): Response
81
    {
82
        if ($token) {
83
            // We store the token in session and remove it from the URL, to avoid the URL being
84
            // loaded in a browser and potentially leaking the token to 3rd party JavaScript.
85
            $this->storeTokenInSession($token);
86
87
            return $this->redirectToRoute('app_reset_password');
88
        }
89
90
        $token = $this->getTokenFromSession();
91
        if (null === $token) {
92
            throw $this->createNotFoundException('No reset password token found in the URL or in the session.');
93
        }
94
95
        try {
96
            $user = $this->resetPasswordHelper->validateTokenAndFetchUser($token);
97
        } catch (ResetPasswordExceptionInterface $e) {
98
            $this->addFlash('reset_password_error', sprintf(
99
                'There was a problem validating your reset request - %s',
100
                $e->getReason()
101
            ));
102
103
            return $this->redirectToRoute('app_forgot_password_request');
104
        }
105
106
        // The token is valid; allow the user to change their password.
107
        $form = $this->createForm(ChangePasswordFormType::class);
108
        $form->handleRequest($request);
109
110
        if ($form->isSubmitted() && $form->isValid()) {
111
            // A password reset token should be used only once, remove it.
112
            $this->resetPasswordHelper->removeResetRequest($token);
113
114
            // Encode the plain password, and set it.
115
            $encodedPassword = $passwordEncoder->encodePassword(
116
                $user,
117
                $form->get('plainPassword')->getData()
118
            );
119
120
            $user->setPassword($encodedPassword);
121
            $this->getDoctrine()->getManager()->flush();
122
123
            // The session is cleaned up after the password has been changed.
124
            $this->cleanSessionAfterReset();
125
126
            return $this->redirectToRoute('homepage');
127
        }
128
129
        return $this->render('reset_password/reset.html.twig', [
130
            'resetForm' => $form->createView(),
131
        ]);
132
    }
133
134
    private function processSendingPasswordResetEmail(string $emailFormData, MailerInterface $mailer): RedirectResponse
135
    {
136
        $user = $this->getDoctrine()->getRepository(User::class)->findOneBy([
137
            'email' => $emailFormData,
138
        ]);
139
140
        // Do not reveal whether a user account was found or not.
141
        if (!$user) {
142
            return $this->redirectToRoute('app_check_email');
143
        }
144
145
        try {
146
            $resetToken = $this->resetPasswordHelper->generateResetToken($user);
147
        } catch (ResetPasswordExceptionInterface $e) {
148
            // If you want to tell the user why a reset email was not sent, uncomment
149
            // the lines below and change the redirect to 'app_forgot_password_request'.
150
            // Caution: This may reveal if a user is registered or not.
151
            //
152
            // $this->addFlash('reset_password_error', sprintf(
153
            //     'There was a problem handling your password reset request - %s',
154
            //     $e->getReason()
155
            // ));
156
157
            return $this->redirectToRoute('app_check_email');
158
        }
159
160
        $email = (new TemplatedEmail())
161
            ->from(new Address('[email protected]', 'Set Later'))
162
            ->to($user->getEmail())
163
            ->subject('Passwort zurücksetzen')
164
            ->htmlTemplate('reset_password/email.html.twig')
165
            ->context([
166
                'resetToken' => $resetToken,
167
                'user' => $user,
168
            ])
169
        ;
170
171
        $mailer->send($email);
172
173
        // Store the token object in session for retrieval in check-email route.
174
        $this->setTokenObjectInSession($resetToken);
175
176
        return $this->redirectToRoute('app_check_email');
177
    }
178
}
179