Passed
Pull Request — master (#27)
by Mathieu
26:55
created

AbstractSecurityController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 4
rs 10
1
<?php
2
3
namespace Smart\AuthenticationBundle\Controller;
4
5
use Smart\AuthenticationBundle\Email\ForgotPasswordEmail;
6
use Smart\AuthenticationBundle\Security\Form\Type\ResetPasswordType;
7
use Smart\AuthenticationBundle\Security\Form\Type\UserProfileType;
8
use Smart\AuthenticationBundle\Form\Type\Security\ForgotPasswordType;
9
use Smart\AuthenticationBundle\Security\SmartUserInterface;
10
use Smart\AuthenticationBundle\Security\Token;
11
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
12
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
13
use Symfony\Component\DependencyInjection\ContainerInterface;
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpFoundation\Response;
16
use Symfony\Component\Mailer\MailerInterface;
17
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
18
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
19
use Yokai\SecurityTokenBundle\Exception\TokenNotFoundException;
20
use Yokai\SecurityTokenBundle\Exception\TokenConsumedException;
21
use Yokai\SecurityTokenBundle\Exception\TokenExpiredException;
22
use Yokai\SecurityTokenBundle\Manager\TokenManagerInterface;
23
24
/**
25
 * @author Nicolas Bastien <[email protected]>
26
 *
27
 * Fix phpstan error cf. https://github.com/phpstan/phpstan/issues/3200
28
 * @property ContainerInterface $container
29
 */
30
class AbstractSecurityController extends Controller
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Bundle\Framework...e\Controller\Controller has been deprecated: since Symfony 4.2, use "Symfony\Bundle\FrameworkBundle\Controller\AbstractController" instead. ( Ignorable by Annotation )

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

30
class AbstractSecurityController extends /** @scrutinizer ignore-deprecated */ Controller
Loading history...
31
{
32
    /**
33
     * Define application context, override this in your controller
34
     * @var string
35
     */
36
    protected $context;
37
38
    /**
39
     * @var TokenManagerInterface
40
     */
41
    protected $tokenManager;
42
43
    /**
44
     * @var MailerInterface
45
     */
46
    protected $mailer;
47
48
    public function __construct(TokenManagerInterface $tokenManager, MailerInterface $mailer)
49
    {
50
        $this->tokenManager = $tokenManager;
51
        $this->mailer = $mailer;
52
    }
53
54
    /**
55
     * @deprecated
56
     * @return Response
57
     */
58
    public function loginAction()
59
    {
60
        return $this->login();
61
    }
62
63
    /**
64
     * @deprecated
65
     * @param Request $request
66
     *
67
     * @return Response
68
     */
69
    public function forgotPasswordAction(Request $request)
70
    {
71
        return $this->forgotPassword($request);
72
    }
73
74
    /**
75
     * @deprecated
76
     * @param Request $request
77
     *
78
     * @return Response
79
     */
80
    public function resetPasswordAction(Request $request)
81
    {
82
        return $this->resetPassword($request);
83
    }
84
85
    /**
86
     * @deprecated
87
     * @param Request $request
88
     *
89
     * @return Response
90
     */
91
    public function profileAction(Request $request)
92
    {
93
        return $this->profile($request);
94
    }
95
96
    /**
97
     * @return Response
98
     */
99
    public function login()
100
    {
101
        $helper = $this->getAuthenticationUtils();
102
103
        return $this->render($this->context . '/security/login.html.twig', [
104
            'last_username' => $helper->getLastUsername(),
105
            'error'         => $helper->getLastAuthenticationError(),
106
            'layout_template' => $this->context . '/empty_layout.html.twig',
107
            'security_login_check_url' => $this->generateUrl($this->context . '_security_login_check'),
108
            'security_forgot_password_url' => $this->generateUrl($this->context . '_security_forgot_password'),
109
        ]);
110
    }
111
112
    /**
113
     * @param Request $request
114
     *
115
     * @return Response
116
     */
117
    public function forgotPassword(Request $request)
118
    {
119
        $form =  $this->createForm(ForgotPasswordType::class);
120
        $form->handleRequest($request);
121
122
        if (!$form->isSubmitted() || !$form->isValid()) {
123
            return $this->render(
124
                $this->context . '/security/forgot_password.html.twig',
125
                [
126
                    'form' => $form->createView(),
127
                    'security_login_form_url' => $this->generateUrl($this->context . '_security_login_form'),
128
                    'security_forgot_password_url' => $this->generateUrl($this->context . '_security_forgot_password'),
129
                ]
130
            );
131
        }
132
133
        try {
134
            $user = $this->get($this->context . '_user_provider')->loadUserByUsername($form->get('email')->getData());
135
136
            if ($user instanceof SmartUserInterface) {
137
                $token = $this->tokenManager->create(Token::RESET_PASSWORD, $user);
138
139
                $this->mailer->send($this->getForgotPasswordEmail([
140
                    'from' => $this->container->getParameter('app.mail_from'),
141
                    'subject' => $this->translate('security.forgot_password.subject', [], 'email'),
142
                    'context' => $this->context,
143
                    'token' => $token->getValue(),
144
                    'domain' => $this->getDomain(),
145
                    'security_reset_password_route' => $this->context . '_security_reset_password'
146
                ], $user->getEmail()));
147
148
                $this->addFlash('success', 'flash.forgot_password.success');
149
            }
150
        } catch (UsernameNotFoundException $e) {
151
            $this->addFlash('error', 'flash.forgot_password.unknown');
152
        }
153
154
        return $this->redirectToRoute($this->context . '_security_login_form');
155
    }
156
157
    /**
158
     * This provide a default email for the forgot password
159
     *
160
     * @param array<mixed> $parameters
161
     * @param string $email
162
     *
163
     * @return TemplatedEmail
164
     */
165
    protected function getForgotPasswordEmail(array $parameters, $email)
166
    {
167
        return new ForgotPasswordEmail($parameters, $email);
168
    }
169
170
    /**
171
     * @param Request $request
172
     *
173
     * @return Response
174
     */
175
    public function resetPassword(Request $request)
176
    {
177
        if ($this->getUser()) {
178
            return $this->redirectToRoute($this->context . '_dashboard');
179
        }
180
181
        if (!$request->query->has('token')) {
182
            $this->addFlash('error', 'flash.security.invalid_token');
183
184
            return $this->redirectToRoute($this->context . '_security_login_form');
185
        }
186
187
        try {
188
            $token = $this->tokenManager->get(Token::RESET_PASSWORD, $request->query->get('token'));
189
        } catch (TokenNotFoundException $e) {
190
            $this->addFlash('error', 'flash.security.token_not_found');
191
            return $this->redirectToRoute($this->context . '_security_login_form');
192
        } catch (TokenExpiredException $e) {
193
            $this->addFlash('error', 'flash.security.token_expired');
194
            return $this->redirectToRoute($this->context . '_security_login_form');
195
        } catch (TokenConsumedException $e) {
196
            $this->addFlash('error', 'flash.security.token_used');
197
            return $this->redirectToRoute($this->context . '_security_login_form');
198
        }
199
200
        /** @var SmartUserInterface $user */
201
        $user = $this->tokenManager->getUser($token);
202
203
        $form =  $this->createForm(ResetPasswordType::class, $user);
204
        $form->handleRequest($request);
205
206
        if (!$form->isSubmitted() || !$form->isValid()) {
207
            return $this->render(
208
                $this->context . '/security/reset_password.html.twig',
209
                [
210
                    'token' => $token->getValue(),
211
                    'form' => $form->createView(),
212
                    'security_reset_password_route' => $this->context . '_security_reset_password'
213
                ]
214
            );
215
        }
216
217
        try {
218
            if (null !== $user->getPlainPassword()) {
0 ignored issues
show
introduced by
The condition null !== $user->getPlainPassword() is always true.
Loading history...
219
                $this->updateUser($user);
220
                $this->tokenManager->consume($token);
221
            }
222
            $this->addFlash('success', 'flash.reset_password.success');
223
        } catch (\Exception $e) {
224
            $this->addFlash('error', 'flash.reset_password.error');
225
        }
226
227
        return $this->redirectToRoute($this->context . '_security_login_form');
228
    }
229
230
    /**
231
     * @param Request $request
232
     *
233
     * @return Response
234
     */
235
    public function profile(Request $request)
236
    {
237
        /** @var SmartUserInterface $user */
238
        $user = $this->getUser();
239
240
        $form = $this->createForm(UserProfileType::class, $user, []);
241
242
        $form->handleRequest($request);
243
244
        if (!$form->isSubmitted() || !$form->isValid()) {
245
            return $this->render($this->context . '/security/profile.html.twig', [
246
                'base_template' => $this->get('sonata.admin.pool')->getTemplate('layout'),
247
                'admin_pool'    => $this->get('sonata.admin.pool'),
248
                'form'          => $form->createView(),
249
                'security_profile_url' => $this->generateUrl('admin_security_profile'),
250
            ]);
251
        }
252
253
        $this->updateUser($user);
254
255
        $this->addFlash('success', $this->translate('profile_edit.processed', [], 'security'));
256
257
        return $this->redirectToRoute('sonata_admin_dashboard');
258
    }
259
260
    /**
261
     * @return AuthenticationUtils
262
     */
263
    private function getAuthenticationUtils()
264
    {
265
        return $this->get('security.authentication_utils');
266
    }
267
268
    /**
269
     * @param string      $id         The message id (may also be an object that can be cast to string)
270
     * @param array<array>       $parameters An array of parameters for the message
271
     * @param string|null $domain     The domain for the message or null to use the default
272
     *
273
     * @return string
274
     */
275
    protected function translate($id, array $parameters = array(), $domain = null)
276
    {
277
        return $this->get('translator')->trans($id, $parameters, $domain);
278
    }
279
280
    /**
281
     * @param SmartUserInterface $user
282
     *
283
     * @return void
284
     */
285
    protected function updateUser(SmartUserInterface $user)
286
    {
287
        if (null !== $user->getPlainPassword()) {
0 ignored issues
show
introduced by
The condition null !== $user->getPlainPassword() is always true.
Loading history...
288
            $encoder = $this->get('security.password_encoder');
289
            $user->setPassword(
290
                $encoder->encodePassword($user, $user->getPlainPassword())
291
            );
292
        }
293
294
        $manager = $this->getDoctrine()->getManager();
295
        $manager->persist($user);
296
        $manager->flush();
297
    }
298
299
    /**
300
     * Override this method if your application use custom domain
301
     *
302
     * @return string
303
     */
304
    protected function getDomain()
305
    {
306
        return $this->container->getParameter('domain');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->container->getParameter('domain') also could return the type array|boolean which is incompatible with the documented return type string.
Loading history...
307
    }
308
}
309