Completed
Push — master ( 19eae0...af0e0d )
by Florian
13s queued 11s
created

SecurityController::authenticate()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
c 0
b 0
f 0
dl 0
loc 22
rs 8.8333
cc 7
nc 7
nop 5
1
<?php
2
3
/*
4
 * This file is part of the TheAlternativeZurich/events project.
5
 *
6
 * (c) Florian Moser <[email protected]>
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
namespace App\Controller;
13
14
use App\Controller\Base\BaseDoctrineController;
15
use App\Entity\User;
16
use App\Form\User\LoginType;
17
use App\Form\User\RegisterType;
18
use App\Helper\HashHelper;
19
use App\Security\UserToken;
20
use App\Service\Interfaces\EmailServiceInterface;
21
use LogicException;
22
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
23
use Symfony\Component\HttpFoundation\RedirectResponse;
24
use Symfony\Component\HttpFoundation\Request;
25
use Symfony\Component\HttpFoundation\Response;
26
use Symfony\Component\Routing\Annotation\Route;
27
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
28
use Symfony\Contracts\Translation\TranslatorInterface;
29
30
class SecurityController extends BaseDoctrineController
31
{
32
    /**
33
     * @Route("/create", name="create")
34
     */
35
    public function create(Request $request, GuardAuthenticatorHandler $guardHandler, TranslatorInterface $translator): Response
36
    {
37
        if (null !== $this->getUser()) {
38
            return $this->redirectToRoute('index');
39
        }
40
41
        $user = new User();
42
        $form = $this->createForm(RegisterType::class, $user)
43
            ->add('submit', SubmitType::class, ['translation_domain' => 'security', 'label' => 'create.submit']);
44
        $form->handleRequest($request);
45
        if ($form->isSubmitted() && $form->isValid()) {
46
            $userRepository = $this->getDoctrine()->getRepository(User::class);
47
            $existingUser = $userRepository->findOneBy(['email' => $user->getEmail()]);
48
            if ($existingUser) {
49
                $message = $translator->trans('create.error.already_registered', [], 'security');
50
                $this->displayError($message);
51
52
                return $this->redirectToRoute('authenticate');
53
            }
54
55
            $this->fastSave($user);
56
57
            $message = $translator->trans('create.success.welcome', [], 'security');
58
            $this->displaySuccess($message);
59
60
            return $this->loginAndRedirect($user, $guardHandler, $request);
61
        }
62
63
        return $this->render('security/create.html.twig', ['form' => $form->createView()]);
64
    }
65
66
    /**
67
     * @Route("/authenticate/{authenticationHash}", defaults={"authenticationHash"=null}, name="authenticate")
68
     */
69
    public function authenticate(?string $authenticationHash, Request $request, GuardAuthenticatorHandler $guardHandler, EmailServiceInterface $emailService, TranslatorInterface $translator): Response
70
    {
71
        if (null !== $authenticationHash && HashHelper::HASH_LENGTH === mb_strlen($authenticationHash)) {
72
            $response = $this->tryLogin($authenticationHash, $guardHandler, $request, $translator);
73
            if ($response instanceof Response) {
74
                return $response;
75
            }
76
        }
77
78
        if (null !== $this->getUser()) {
79
            return $this->redirectToRoute('index');
80
        }
81
82
        $user = new User();
83
        $form = $this->createForm(LoginType::class, $user)
84
            ->add('submit', SubmitType::class, ['translation_domain' => 'security', 'label' => 'authenticate.submit']);
85
        $form->handleRequest($request);
86
        if ($form->isSubmitted() && $form->isValid()) {
87
            $this->sendAuthenticationEmail($user, $emailService, $translator);
88
        }
89
90
        return $this->render('security/authenticate.html.twig', ['form' => $form->createView()]);
91
    }
92
93
    /**
94
     * @Route("/logout", name="logout")
95
     */
96
    public function logout()
97
    {
98
        throw new LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
99
    }
100
101
    private function sendAuthenticationEmail(User $user, EmailServiceInterface $emailService, TranslatorInterface $translator): void
102
    {
103
        $userRepository = $this->getDoctrine()->getRepository(User::class);
104
        $existingUser = $userRepository->findOneBy(['email' => $user->getEmail()]);
105
        if ($existingUser && !$emailService->sendAuthenticateLink($existingUser)) {
106
            $message = $translator->trans('errors.email_could_not_be_sent', [], 'email');
107
            $this->displayError($message);
108
        } else {
109
            // also show success message if user is not found to make it intransparent who is registered
110
            $message = $translator->trans('authenticate.success.sent_authentication_link', [], 'security');
111
            $this->displaySuccess($message);
112
        }
113
    }
114
115
    private function tryLogin(string $authenticationHash, GuardAuthenticatorHandler $guardHandler, Request $request, TranslatorInterface $translator): ?RedirectResponse
116
    {
117
        $userRepository = $this->getDoctrine()->getRepository(User::class);
118
        $user = $userRepository->findOneBy(['authenticationHash' => $authenticationHash]);
119
120
        if (null !== $user) {
121
            $message = $translator->trans('authenticate.success.authentication_successful', [], 'security');
122
            $this->displaySuccess($message);
123
124
            if (!$user->getIsEmailConfirmed() && $this->getUser()) {
125
                $user->setIsEmailConfirmed(true);
126
                $this->fastSave($user);
127
128
                $message = $translator->trans('authenticate.success.email_confirmed', [], 'security');
129
                $this->displaySuccess($message);
130
            }
131
132
            return $this->loginAndRedirect($user, $guardHandler, $request);
133
        } else {
134
            $message = $translator->trans('authenticate.errors.authentication_code_invalid', [], 'security');
135
            $this->displayError($message);
136
137
            return null;
138
        }
139
    }
140
141
    private function loginAndRedirect(User $user, GuardAuthenticatorHandler $guardHandler, Request $request): RedirectResponse
142
    {
143
        $userToken = new UserToken($user);
144
        $guardHandler->authenticateWithToken($userToken, $request, 'main');
145
146
        $redirectPathKey = '_security.main.target_path';
147
        if ($request->getSession()->has($redirectPathKey)) {
148
            $value = $request->getSession()->get($redirectPathKey);
149
            $request->getSession()->remove($redirectPathKey);
150
151
            return $this->redirect($value);
152
        }
153
154
        return $this->redirectToRoute('index');
155
    }
156
}
157