ResetPasswordController::checkEmail()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 10
rs 10
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\PasswordHasher\Hasher\UserPasswordHasherInterface;
16
use Symfony\Component\Routing\Annotation\Route;
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, UserPasswordHasherInterface $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->hashPassword(
116
                $user,
117
                $form->get('plainPassword')->getData()
118
            );
119
120
            $user->setPassword($encodedPassword);
121
            $this->getDoctrine()->getManager()->flush();
0 ignored issues
show
Deprecated Code introduced by
The function Symfony\Bundle\Framework...ntroller::getDoctrine() has been deprecated: since Symfony 5.4, inject an instance of ManagerRegistry in your controller instead ( Ignorable by Annotation )

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

121
            /** @scrutinizer ignore-deprecated */ $this->getDoctrine()->getManager()->flush();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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([
0 ignored issues
show
Deprecated Code introduced by
The function Symfony\Bundle\Framework...ntroller::getDoctrine() has been deprecated: since Symfony 5.4, inject an instance of ManagerRegistry in your controller instead ( Ignorable by Annotation )

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

136
        $user = /** @scrutinizer ignore-deprecated */ $this->getDoctrine()->getRepository(User::class)->findOneBy([

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
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