From 99d66c71e0a551eaacc087e10da3afc5aa93f813 Mon Sep 17 00:00:00 2001 From: Camille Simiand <camille.simiand@tetras-libre.fr> Date: Tue, 30 Nov 2021 15:44:22 +0100 Subject: [PATCH] Various fixes: - Login tests - log in check when user account not verified - remove duplicated bundles in composer.json - correct sign in -> log in in template --- composer.json | 2 - config/packages/security.yaml | 1 + src/Controller/SecurityController.php | 17 ++++-- src/Security/AppCustomAuthenticator.php | 1 - src/Security/UserChecker.php | 37 ++++++++++++ templates/security/login.html.twig | 14 ++--- tests/functional/LoginTest.php | 76 ++++++++++++++++++++----- translations/messages.en.yaml | 2 + 8 files changed, 122 insertions(+), 28 deletions(-) create mode 100644 src/Security/UserChecker.php create mode 100644 translations/messages.en.yaml diff --git a/composer.json b/composer.json index 4c16ac3..1b4e852 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 c1ebf67..bfb5e2f 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 6fdeced..57a3c87 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 d9ddbb8..05d6c1b 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 0000000..36201e3 --- /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 5a0aa54..58285ed 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 16b0e7d..b6e06d8 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 0000000..8369909 --- /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 -- GitLab