diff --git a/composer.json b/composer.json index 4c16ac377dc71301f0b360a11a4d8f93f32fa5b0..1b4e852bbbbac4b087e96ef41c3ba1377eb2ec26 100644 --- a/composer.json +++ b/composer.json @@ -53,8 +53,6 @@ "codeception/module-asserts": "^1.3", "codeception/module-phpbrowser": "^1.0", "codeception/module-symfony": "^2.0", - "dama/doctrine-test-bundle": "^6.7", - "doctrine/doctrine-fixtures-bundle": "^3.4", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.2", "phpstan/phpstan-doctrine": "^1.0", diff --git a/config/packages/security.yaml b/config/packages/security.yaml index c1ebf675762c2ace25b03031b433633872dbd690..bfb5e2fd38196cb317204f5d9ca10a6bd675a606 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -21,6 +21,7 @@ security: lazy: true provider: app_user_provider custom_authenticator: App\Security\AppCustomAuthenticator + user_checker: App\Security\UserChecker logout: path: app_logout # where to redirect after logout diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index 6fdecedba0359586805a7ea89a04a2a48ea411cc..57a3c87ffc9f08aee39db66185a1d989eae1ac96 100644 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -2,10 +2,13 @@ namespace App\Controller; +use App\Entity\User; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; +use Symfony\Contracts\Translation\TranslatorInterface; class SecurityController extends AbstractController { @@ -14,16 +17,22 @@ class SecurityController extends AbstractController */ public function login(AuthenticationUtils $authenticationUtils): Response { - if ($this->getUser()) { - return $this->redirectToRoute('capsule_list'); - } + if ($this->getUser()) { + return $this->redirectToRoute('capsule_list'); + } // get the login error if there is one $error = $authenticationUtils->getLastAuthenticationError(); // last username entered by the user $lastUsername = $authenticationUtils->getLastUsername(); - return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]); + return $this->render( + 'security/login.html.twig', + [ + 'last_username' => $lastUsername, + 'error' => $error + ] + ); } /** diff --git a/src/Security/AppCustomAuthenticator.php b/src/Security/AppCustomAuthenticator.php index d9ddbb817099e1c7f414a1d34d709598661e615c..05d6c1b253b3d7e410871c958c11019a4297cd69 100644 --- a/src/Security/AppCustomAuthenticator.php +++ b/src/Security/AppCustomAuthenticator.php @@ -50,7 +50,6 @@ class AppCustomAuthenticator extends AbstractLoginFormAuthenticator return new RedirectResponse($targetPath); } - // For example: return new RedirectResponse($this->urlGenerator->generate('capsule_list')); } diff --git a/src/Security/UserChecker.php b/src/Security/UserChecker.php new file mode 100644 index 0000000000000000000000000000000000000000..36201e37db30f49a3bb6f9ea204e20480286e2a6 --- /dev/null +++ b/src/Security/UserChecker.php @@ -0,0 +1,37 @@ +<?php + +namespace App\Security; + +use App\Entity\User; +use Symfony\Component\Security\Core\Exception\AccountStatusException; +use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; +use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +class UserChecker implements UserCheckerInterface +{ + private TranslatorInterface $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + + public function checkPreAuth(UserInterface $user) + { + } + + public function checkPostAuth(UserInterface $user) + { + if (!$user instanceof User) { + return; + } + + if (!$user->isVerified()) { + throw new CustomUserMessageAccountStatusException( + $this->translator->trans('login.account_disabled_feedback') + ); + } + } +} diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig index 5a0aa5463161ff713c0df3d9b9953e695321ff9f..58285ed0cdffbc569d4bbf52489795d577a26104 100644 --- a/templates/security/login.html.twig +++ b/templates/security/login.html.twig @@ -1,6 +1,6 @@ {% extends 'layout.html.twig' %} -{% block title %}Log in!{% endblock %} +{% block title %}Log in{% endblock %} {% block body %} <form method="post" class="d-flex flex-column justify-content-center"> @@ -17,17 +17,15 @@ </div> {% endif %} - <h1 class="mb-3 m-auto col-6">Please sign in</h1> - <div class="form-group d-flex flex-column m-auto mb-4 col-6"> + <div class="form-group d-flex flex-column m-auto mb-4 mt-4 col-6"> <label for="inputEmail" class="form-label">Email</label> <input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control" autocomplete="email" required autofocus> - </div> + </div> <div class="form-group d-flex flex-column m-auto mb-4 col-6"> - <label for="inputPassword" class="form-label">Password</label> - <input type="password" name="password" id="inputPassword" class="form-control" autocomplete="current-password" required> + <label for="inputPassword" class="form-label">Password</label> + <input type="password" name="password" id="inputPassword" class="form-control" autocomplete="current-password" required> </div> - <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}" > @@ -44,7 +42,7 @@ #} <button class="btn btn-primary col-2 m-auto" type="submit"> - Sign in + Log in </button> </form> {% endblock %} diff --git a/tests/functional/LoginTest.php b/tests/functional/LoginTest.php index 16b0e7ddbd2dbdc9217a53f49ec3c6a43039472e..b6e06d897505e30998a7cd57de9bdc95c2889970 100644 --- a/tests/functional/LoginTest.php +++ b/tests/functional/LoginTest.php @@ -2,28 +2,78 @@ namespace App\Tests\Controller; -use App\Repository\UserRepository; +use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\DomCrawler\Form; class LoginTest extends WebTestCase { - public function testSomething(): void - { - $client = static::createClient(); - $crawler = $client->request('GET', '/login'); + private KernelBrowser $client; + private Form $form; + protected function setUp(): void + { + $this->client = static::createClient(); + $crawler = $this->client->request('GET', '/login'); $this->assertResponseIsSuccessful(); - $client->enableProfiler(); - $submit = $crawler->selectButton('Sign in'); - $form = $submit->form(); + $this->client->enableProfiler(); + $submit_button = $crawler->selectButton('Log in'); + $this->form = $submit_button->form(); + } + + public function testRegisteredUserWithEnabledAccountIsRedirectedToCapsulesPageWhenSubmittingLoginForm(): void + { + $this->form['email'] = 'defaultUser@localhost.com'; + $this->form['password'] = "password"; + + $this->client->submit($this->form); - $form['email'] = 'defaultUser@localhost.com'; - $form['password'] = "password"; + $this->assertResponseRedirects( + '/my_capsules', + 302, + 'Once the user is logged in, He should be redirected to its capsules lists' + ); + + $this->client->followRedirect(); - $client->submit($form); - $this->assertResponseRedirects('/my_capsules', 302, 'Once the user is logged in, He should be redirected to its capsules lists'); - $client->followRedirect(); $this->assertResponseIsSuccessful('/my_capsules'); } + + public function testRegisteredUserWithDisabledAccountIsRedirectedToLoginPageWhenSubmittingLoginForm(): void + { + $this->form['email'] = 'notVerified@localhost.com'; + $this->form['password'] = "password"; + + $this->client->submit($this->form); + + $this->assertResponseRedirects( + '/login', + 302, + ' + A registered user with a disabled account is redirected to login page when trying to log in' + ); + + $this->client->followRedirect(); + + $this->assertResponseIsSuccessful('/login'); + } + + public function testNonRegisteredUserIsRedirectedToLoginPageWhenSubmittingLoginForm(): void + { + $this->form['email'] = 'notRegistered@localhost.com'; + $this->form['password'] = "password"; + + $this->client->submit($this->form); + + $this->assertResponseRedirects( + '/login', + 302, + 'A non registered user is redirected to login page when trying to log in' + ); + + $this->client->followRedirect(); + + $this->assertResponseIsSuccessful('/login'); + } } diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8369909df15e9a8e38809a07e45e9b0877cdfed5 --- /dev/null +++ b/translations/messages.en.yaml @@ -0,0 +1,2 @@ +login: + account_disabled_feedback: Your user account is disabled. Please click on the link your receive by email to validate your registration. \ No newline at end of file