From 76f7228944b2932a25c9863e78a367abed5be47e Mon Sep 17 00:00:00 2001 From: Laurent Muller Date: Mon, 9 Dec 2024 16:18:01 +0100 Subject: [PATCH] Updated parameters templates. --- LICENSE.md | 2 +- composer.lock | 38 ++++++------- src/Form/Admin/ApplicationParametersType.php | 3 + src/Form/FormHelper.php | 10 ++-- src/Form/User/AbstractUserCaptchaType.php | 13 +++-- src/Form/User/UserLoginType.php | 8 +++ src/Security/LoginFormAuthenticator.php | 42 ++++++++------ src/Traits/PropertyServiceTrait.php | 15 ++++- templates/admin/parameters.html.twig | 16 +++--- templates/fields.html.twig | 4 +- templates/parameters/_parameters.html.twig | 5 +- templates/user/user_parameters.html.twig | 8 +-- .../CalculationEditStateTypeTest.php | 2 + .../Form/Calculation/CalculationTypeTest.php | 2 + .../CalculationStateTrait.php | 2 - tests/Form/Type/ReCaptchaTypeTest.php | 2 - tests/Form/User/ProfileEditTypeTest.php | 2 - tests/Form/User/RoleChoiceTypeTest.php | 2 - tests/Security/LoginFormAuthenticatorTest.php | 56 +++++++++++++------ tests/Service/ApplicationServiceTest.php | 2 - tests/Service/OpenWeatherServiceTest.php | 3 - translations/security.fr_CH.yaml | 5 ++ vendor-bin/psalm/composer.lock | 24 ++++---- 23 files changed, 156 insertions(+), 110 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 6229e6933..b383e6dba 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -36,7 +36,7 @@ le désirent les éléments de l'application. Aucun droit d'utilisation supplémentaire n'est accordé au preneur de contrat et aux utilisateurs autorisés. Ils n'ont en particulier pas le droit de modifier cette application, de la corriger, de la développer, de la traduire, de la déchiffrer ou de la -décompiler ni de la vendre, la louer, la prêter, la transmettre et ni, surtout, +décompiler ni de la vendre, la louer, la prêter, la transmettre et ni surtout, de permettre à des tiers de l'utiliser. ## Mises à jour, développements, assistance technique diff --git a/composer.lock b/composer.lock index eb2a2ee7c..51489a8da 100644 --- a/composer.lock +++ b/composer.lock @@ -564,29 +564,27 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "1.4.10 || 2.0.3", + "phpstan/phpstan-phpunit": "^1.0 || ^2", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" + "psr/log": "^1 || ^2 || ^3" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -594,7 +592,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + "Doctrine\\Deprecations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -605,9 +603,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + "source": "https://github.com/doctrine/deprecations/tree/1.1.4" }, - "time": "2024-01-30T19:34:25+00:00" + "time": "2024-12-07T21:18:45+00:00" }, { "name": "doctrine/doctrine-bundle", @@ -2348,16 +2346,16 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "3.5.0", + "version": "3.6.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "fb74dcdfa5b538763ab980e977529bc783039aec" + "reference": "bce5db99872f9613121c3ad033c43318a3789396" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/fb74dcdfa5b538763ab980e977529bc783039aec", - "reference": "fb74dcdfa5b538763ab980e977529bc783039aec", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/bce5db99872f9613121c3ad033c43318a3789396", + "reference": "bce5db99872f9613121c3ad033c43318a3789396", "shasum": "" }, "require": { @@ -2391,7 +2389,7 @@ "phpcompatibility/php-compatibility": "^9.3", "phpstan/phpstan": "^1.1", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.6 || ^10.5", + "phpunit/phpunit": "^10.5", "squizlabs/php_codesniffer": "^3.7", "tecnickcom/tcpdf": "^6.5" }, @@ -2446,9 +2444,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/3.5.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/3.6.0" }, - "time": "2024-11-22T06:41:00+00:00" + "time": "2024-12-08T15:04:12+00:00" }, { "name": "phpoffice/phpword", diff --git a/src/Form/Admin/ApplicationParametersType.php b/src/Form/Admin/ApplicationParametersType.php index 0bc15beeb..d80a8c54d 100644 --- a/src/Form/Admin/ApplicationParametersType.php +++ b/src/Form/Admin/ApplicationParametersType.php @@ -51,14 +51,17 @@ protected function addSections(FormHelper $helper): void private function addCustomerSection(FormHelper $helper): void { $helper->field(PropertyServiceInterface::P_CUSTOMER_NAME) + ->updateOption('prepend_icon', 'fa-solid fa-user-group') ->updateAttribute('spellcheck', 'false') ->addTextType(); $helper->field(PropertyServiceInterface::P_CUSTOMER_ADDRESS) + ->updateOption('prepend_icon', 'fa-solid fa-location-dot') ->notRequired() ->addTextType(); $helper->field(PropertyServiceInterface::P_CUSTOMER_ZIP_CITY) + ->updateOption('prepend_icon', 'fa-solid fa-map-location-dot') ->notRequired() ->addTextType(); diff --git a/src/Form/FormHelper.php b/src/Form/FormHelper.php index c82b71932..bb9ed62f5 100644 --- a/src/Form/FormHelper.php +++ b/src/Form/FormHelper.php @@ -242,7 +242,7 @@ public function addDateType(): self public function addEmailType(): self { return $this->updateAttribute('inputmode', 'email') - ->updateOption('prepend_icon', 'fa-fw fa-solid fa-at') + ->updateOption('prepend_icon', 'fa-solid fa-at') ->updateOption('prepend_class', 'input-group-email') ->add(EmailType::class); } @@ -275,7 +275,7 @@ public function addEnumType(string $class): self public function addFaxType(?string $pattern = null): self { return $this->updateAttribute('pattern', $pattern) - ->updateOption('prepend_icon', 'fa-fw fa-solid fa-fax') + ->updateOption('prepend_icon', 'fa-solid fa-fax') ->updateOption('prepend_class', 'input-group-fax') ->add(TelType::class); } @@ -432,7 +432,7 @@ public function addSimulateAndConfirmType(TranslatorInterface $translator, bool */ public function addTelType(?string $pattern = null): self { - return $this->updateOption('prepend_icon', 'fa-fw fa-solid fa-phone') + return $this->updateOption('prepend_icon', 'fa-solid fa-phone') ->updateOption('prepend_class', 'input-group-phone') ->updateAttribute('inputmode', 'tel') ->updateAttribute('pattern', $pattern) @@ -485,7 +485,7 @@ public function addTrueFalseType( public function addUrlType(string $protocol = 'https'): self { return $this->updateOption('default_protocol', $protocol) - ->updateOption('prepend_icon', 'fa-fw fa-solid fa-globe') + ->updateOption('prepend_icon', 'fa-solid fa-globe') ->updateOption('prepend_class', 'input-group-url') ->updateAttribute('inputmode', 'url') ->add(UrlType::class); @@ -496,7 +496,7 @@ public function addUrlType(string $protocol = 'https'): self */ public function addUserNameType(string|bool $autocomplete = 'username'): self { - return $this->updateOption('prepend_icon', 'fa-fw fa-regular fa-user') + return $this->updateOption('prepend_icon', 'fa-regular fa-user') ->autocomplete($autocomplete) ->updateAttributes([ 'minLength' => UserInterface::MIN_USERNAME_LENGTH, diff --git a/src/Form/User/AbstractUserCaptchaType.php b/src/Form/User/AbstractUserCaptchaType.php index a5d92bb09..97d896039 100644 --- a/src/Form/User/AbstractUserCaptchaType.php +++ b/src/Form/User/AbstractUserCaptchaType.php @@ -48,12 +48,13 @@ public function getBlockPrefix(): string */ protected function addFormFields(FormHelper $helper): void { - if ($this->application->isDisplayCaptcha()) { - $helper->field(SecurityAttributes::CAPTCHA_FIELD) - ->label('captcha.label') - ->constraints(new NotBlank(), new Captcha()) - ->updateOption('image', $this->service->generateImage()) - ->add(CaptchaImageType::class); + if (!$this->application->isDisplayCaptcha()) { + return; } + $helper->field(SecurityAttributes::CAPTCHA_FIELD) + ->label('captcha.label') + ->constraints(new NotBlank(), new Captcha()) + ->updateOption('image', $this->service->generateImage()) + ->add(CaptchaImageType::class); } } diff --git a/src/Form/User/UserLoginType.php b/src/Form/User/UserLoginType.php index e7ccc7ea7..9c18d5652 100644 --- a/src/Form/User/UserLoginType.php +++ b/src/Form/User/UserLoginType.php @@ -15,12 +15,20 @@ use App\Form\FormHelper; use App\Security\SecurityAttributes; +use Symfony\Component\OptionsResolver\OptionsResolver; /** * User login type. */ class UserLoginType extends AbstractUserCaptchaType { + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'csrf_protection' => false, + ]); + } + protected function addFormFields(FormHelper $helper): void { $helper->field(SecurityAttributes::USER_FIELD) diff --git a/src/Security/LoginFormAuthenticator.php b/src/Security/LoginFormAuthenticator.php index 38008072f..f224ee6ce 100644 --- a/src/Security/LoginFormAuthenticator.php +++ b/src/Security/LoginFormAuthenticator.php @@ -18,9 +18,7 @@ use App\Service\CaptchaImageService; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; @@ -45,10 +43,13 @@ public function __construct( public function authenticate(Request $request): Passport { $this->validateCaptcha($request); - [$user, $password, $token] = $this->getCredentials($request); + + $userIdentifier = $this->getUserIdentifier($request); + $password = $this->getPassword($request); + $token = $this->getToken($request); return new Passport( - new UserBadge($user, $this->repository->loadUserByIdentifier(...)), + new UserBadge($userIdentifier, $this->repository->loadUserByIdentifier(...)), new PasswordCredentials($password), [ new RememberMeBadge(), @@ -75,28 +76,37 @@ protected function getLoginUrl(Request $request): string return $this->httpUtils->generateUri($request, SecurityAttributes::LOGIN_ROUTE); } - /** - * @psalm-return list{non-empty-string, non-empty-string, non-empty-string} - */ - private function getCredentials(Request $request): array + private function getPassword(Request $request): string { - $user = \trim($request->request->getString(SecurityAttributes::USER_FIELD)); - if ('' === $user) { - throw new BadCredentialsException('The username must be a non-empty string.'); - } $password = $request->request->getString(SecurityAttributes::PASSWORD_FIELD); if ('' === $password) { - throw new BadCredentialsException('The password must be a non-empty string.'); + throw new CustomUserMessageAuthenticationException('authenticator.empty_password'); } + + return $password; + } + + private function getToken(Request $request): string + { $token = $request->request->getString(SecurityAttributes::LOGIN_TOKEN); if ('' === $token) { - throw new BadRequestHttpException('The token must be a non-empty string.'); + throw new CustomUserMessageAuthenticationException('authenticator.empty_token'); + } + + return $token; + } + + private function getUserIdentifier(Request $request): string + { + $userIdentifier = \trim($request->request->getString(SecurityAttributes::USER_FIELD)); + if ('' === $userIdentifier) { + throw new CustomUserMessageAuthenticationException('authenticator.empty_user'); } if ($request->hasSession()) { - $request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $user); + $request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $userIdentifier); } - return [$user, $password, $token]; + return $userIdentifier; } private function validateCaptcha(Request $request): void diff --git a/src/Traits/PropertyServiceTrait.php b/src/Traits/PropertyServiceTrait.php index 5c058289e..d6ebde414 100644 --- a/src/Traits/PropertyServiceTrait.php +++ b/src/Traits/PropertyServiceTrait.php @@ -26,11 +26,20 @@ */ trait PropertyServiceTrait { - use CacheTrait; + use CacheTrait { + getCacheValue as private getCacheValueFromTrait; + } // The saved cache state property name private const P_CACHE_SAVED = 'cache_saved'; + public function getCacheValue(string $key, mixed $default = null): mixed + { + $this->initialize(); + + return $this->getCacheValueFromTrait($key, $default); + } + public function isActionEdit(): bool { return EntityAction::EDIT === $this->getEditAction(); @@ -193,11 +202,11 @@ protected function getPropertyString(string $name, ?string $default = null): ?st } /** - * Initialize cache properties. + * Initialize cached properties. */ protected function initialize(): void { - if (!$this->getPropertyBoolean(self::P_CACHE_SAVED) && $this->isConnected()) { + if (!(bool) $this->getCacheValueFromTrait(self::P_CACHE_SAVED, false) && $this->isConnected()) { $this->updateAdapter(); } } diff --git a/templates/admin/parameters.html.twig b/templates/admin/parameters.html.twig index 87817488a..0c14e8c4a 100644 --- a/templates/admin/parameters.html.twig +++ b/templates/admin/parameters.html.twig @@ -5,14 +5,14 @@ {%- set title_description = 'parameters.description' -%} {# sections #} {% block sections -%} -{{ _self.collapseLine('customer', form, true) }} -{{ _self.collapseLine('default', form) }} -{{ _self.collapseLine('product', form) }} -{{ _self.collapseLine('display', form) }} -{{ _self.collapseLine('message', form) }} -{{ _self.collapseLine('home', form) }} -{{ _self.collapseLine('options', form) }} +{{ _self.collapseLine('customer', form, 'user-tie', true) }} +{{ _self.collapseLine('default', form, 'file-code') }} +{{ _self.collapseLine('product', form, 'file-alt') }} +{{ _self.collapseLine('display', form, 'display') }} +{{ _self.collapseLine('message', form, 'bell') }} +{{ _self.collapseLine('home', form, 'house-user') }} +{{ _self.collapseLine('options', form, 'list-check') }} {% if is_super_admin %} - {{ _self.collapseLine('security', form, false, options) }} + {{ _self.collapseLine('security', form, 'lock', false, options) }} {% endif %} {%- endblock %} diff --git a/templates/fields.html.twig b/templates/fields.html.twig index a6cfa3933..be2bc35cc 100644 --- a/templates/fields.html.twig +++ b/templates/fields.html.twig @@ -82,13 +82,13 @@
{% if prepend_icon %}
- +
{% endif %} {{- parent() -}} {% if append_icon %}
- +
{% endif %}
diff --git a/templates/parameters/_parameters.html.twig b/templates/parameters/_parameters.html.twig index 0e91f574e..a7edd21e3 100644 --- a/templates/parameters/_parameters.html.twig +++ b/templates/parameters/_parameters.html.twig @@ -1,7 +1,8 @@ {% extends 'cards/card_edit.html.twig' %} +{% from 'macros/_icons.html.twig' import icon as output_icon %} {% form_theme form with _self %} {# macros #} -{% macro collapseLine(href, form, expanded = false, options = null) %} +{% macro collapseLine(href, form, icon, expanded = false, options = null) %} {%- set header_id = 'header_' ~ href -%} {%- set name = 'parameters.group_%s'|format(href)|trans -%} {%- set template = 'parameters/_parameters_%s.html.twig'|format(href) -%} @@ -10,7 +11,7 @@ {%- set title = expanded ? hide : show -%}
- {{- name -}} + {{- output_icon(icon ~ ' fa-fw me-1') -}}{{- name -}}
diff --git a/templates/user/user_parameters.html.twig b/templates/user/user_parameters.html.twig index 8d53c1e66..64bc51bb5 100644 --- a/templates/user/user_parameters.html.twig +++ b/templates/user/user_parameters.html.twig @@ -5,8 +5,8 @@ {%- set title_description = 'user.parameters.description' -%} {# sections #} {% block sections -%} -{{ _self.collapseLine('display', form, true) }} -{{ _self.collapseLine('message', form) }} -{{ _self.collapseLine('home', form) }} -{{ _self.collapseLine('options', form) }} +{{ _self.collapseLine('display', form, 'display', true) }} +{{ _self.collapseLine('message', form, 'bell') }} +{{ _self.collapseLine('home', form, 'house-user') }} +{{ _self.collapseLine('options', form, 'list-check') }} {%- endblock %} diff --git a/tests/Form/Calculation/CalculationEditStateTypeTest.php b/tests/Form/Calculation/CalculationEditStateTypeTest.php index 7583d47bc..9654c5676 100644 --- a/tests/Form/Calculation/CalculationEditStateTypeTest.php +++ b/tests/Form/Calculation/CalculationEditStateTypeTest.php @@ -24,6 +24,7 @@ use App\Service\ApplicationService; use App\Tests\Form\CalculationState\CalculationStateTrait; use App\Tests\Form\EntityTypeTestCase; +use App\Tests\TranslatorMockTrait; use App\Utils\DateUtils; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; @@ -35,6 +36,7 @@ class CalculationEditStateTypeTest extends EntityTypeTestCase { use CalculationStateTrait; + use TranslatorMockTrait; private MockObject&ApplicationService $application; private bool $marginBelow = false; diff --git a/tests/Form/Calculation/CalculationTypeTest.php b/tests/Form/Calculation/CalculationTypeTest.php index 2fe1eeff4..177efdaea 100644 --- a/tests/Form/Calculation/CalculationTypeTest.php +++ b/tests/Form/Calculation/CalculationTypeTest.php @@ -22,6 +22,7 @@ use App\Repository\GroupRepository; use App\Tests\Form\CalculationState\CalculationStateTrait; use App\Tests\Form\EntityTypeTestCase; +use App\Tests\TranslatorMockTrait; use App\Utils\DateUtils; use Doctrine\Common\Collections\ArrayCollection; use PHPUnit\Framework\MockObject\Exception; @@ -33,6 +34,7 @@ class CalculationTypeTest extends EntityTypeTestCase { use CalculationStateTrait; + use TranslatorMockTrait; protected function getData(): array { diff --git a/tests/Form/CalculationState/CalculationStateTrait.php b/tests/Form/CalculationState/CalculationStateTrait.php index 6ed67d6f4..e388619d3 100644 --- a/tests/Form/CalculationState/CalculationStateTrait.php +++ b/tests/Form/CalculationState/CalculationStateTrait.php @@ -17,7 +17,6 @@ use App\Repository\CalculationStateRepository; use App\Tests\Entity\IdTrait; use App\Tests\Form\ManagerRegistryTrait; -use App\Tests\TranslatorMockTrait; use Doctrine\Persistence\ManagerRegistry; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; @@ -31,7 +30,6 @@ trait CalculationStateTrait { use IdTrait; use ManagerRegistryTrait; - use TranslatorMockTrait; private ?CalculationState $editableState = null; private ?CalculationState $notEditableState = null; diff --git a/tests/Form/Type/ReCaptchaTypeTest.php b/tests/Form/Type/ReCaptchaTypeTest.php index 066ae6802..50b7f16b3 100644 --- a/tests/Form/Type/ReCaptchaTypeTest.php +++ b/tests/Form/Type/ReCaptchaTypeTest.php @@ -16,7 +16,6 @@ use App\Form\Type\ReCaptchaType; use App\Service\RecaptchaService; use App\Tests\Form\PreloadedExtensionsTrait; -use App\Tests\TranslatorMockTrait; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; use ReCaptcha\Response; @@ -27,7 +26,6 @@ class ReCaptchaTypeTest extends TypeTestCase { use PreloadedExtensionsTrait; - use TranslatorMockTrait; private ?Request $request = null; private MockObject&RequestStack $requestStack; diff --git a/tests/Form/User/ProfileEditTypeTest.php b/tests/Form/User/ProfileEditTypeTest.php index 1040ab1b3..23ddd3348 100644 --- a/tests/Form/User/ProfileEditTypeTest.php +++ b/tests/Form/User/ProfileEditTypeTest.php @@ -16,7 +16,6 @@ use App\Entity\User; use App\Form\User\ProfileEditType; use App\Tests\Form\EntityTypeTestCase; -use App\Tests\TranslatorMockTrait; use PHPUnit\Framework\MockObject\Exception; /** @@ -24,7 +23,6 @@ */ class ProfileEditTypeTest extends EntityTypeTestCase { - use TranslatorMockTrait; use VichImageTypeTrait; protected function getData(): array diff --git a/tests/Form/User/RoleChoiceTypeTest.php b/tests/Form/User/RoleChoiceTypeTest.php index 0e426cb9f..5230cdb01 100644 --- a/tests/Form/User/RoleChoiceTypeTest.php +++ b/tests/Form/User/RoleChoiceTypeTest.php @@ -17,7 +17,6 @@ use App\Form\User\RoleChoiceType; use App\Interfaces\RoleInterface; use App\Tests\Form\PreloadedExtensionsTrait; -use App\Tests\TranslatorMockTrait; use PHPUnit\Framework\MockObject\Exception; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\Form\Test\TypeTestCase; @@ -25,7 +24,6 @@ class RoleChoiceTypeTest extends TypeTestCase { use PreloadedExtensionsTrait; - use TranslatorMockTrait; public function testSubmitSynchronized(): void { diff --git a/tests/Security/LoginFormAuthenticatorTest.php b/tests/Security/LoginFormAuthenticatorTest.php index 31cffa89e..0985a3baf 100644 --- a/tests/Security/LoginFormAuthenticatorTest.php +++ b/tests/Security/LoginFormAuthenticatorTest.php @@ -25,11 +25,14 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\HttpUtils; @@ -64,8 +67,11 @@ public function testAuthenticate(): void $request = self::createRequest(values: $values); $request->setSession(new Session(new MockArraySessionStorage())); - $actual = $authenticator->authenticate($request); - self::assertInstanceOf(Passport::class, $actual); + $passport = $authenticator->authenticate($request); + self::assertHasBadge($passport, UserBadge::class); + self::assertHasBadge($passport, RememberMeBadge::class); + self::assertHasBadge($passport, PasswordUpgradeBadge::class); + self::assertHasBadge($passport, CsrfTokenBadge::class); } /** @@ -84,47 +90,50 @@ public function testAuthenticateEmptyPassword(): void ]; $request = self::createRequest(values: $values); - self::expectException(BadCredentialsException::class); + self::expectException(CustomUserMessageAuthenticationException::class); + self::expectExceptionMessage('authenticator.empty_password'); $authenticator->authenticate($request); } /** * @throws Exception */ - public function testAuthenticateEmptyUserName(): void + public function testAuthenticateEmptyToken(): void { $httpUtils = $this->createMock(HttpUtils::class); $httpUtils->method('checkRequestPath') ->willReturn(true); $authenticator = $this->createAuthenticator(httpUtils: $httpUtils); $values = [ - SecurityAttributes::USER_FIELD => '', + SecurityAttributes::USER_FIELD => 'username', SecurityAttributes::PASSWORD_FIELD => 'password', - SecurityAttributes::LOGIN_TOKEN => 'token', + SecurityAttributes::LOGIN_TOKEN => '', ]; $request = self::createRequest(values: $values); - self::expectException(BadCredentialsException::class); + self::expectException(CustomUserMessageAuthenticationException::class); + self::expectExceptionMessage('authenticator.empty_token'); $authenticator->authenticate($request); } /** * @throws Exception */ - public function testAuthenticateNoToken(): void + public function testAuthenticateEmptyUserName(): void { $httpUtils = $this->createMock(HttpUtils::class); $httpUtils->method('checkRequestPath') ->willReturn(true); $authenticator = $this->createAuthenticator(httpUtils: $httpUtils); $values = [ - SecurityAttributes::USER_FIELD => 'username', + SecurityAttributes::USER_FIELD => '', SecurityAttributes::PASSWORD_FIELD => 'password', - SecurityAttributes::LOGIN_TOKEN => '', + SecurityAttributes::LOGIN_TOKEN => 'token', ]; $request = self::createRequest(values: $values); - self::expectException(BadRequestHttpException::class); + self::expectException(CustomUserMessageAuthenticationException::class); + self::expectExceptionMessage('authenticator.empty_user'); $authenticator->authenticate($request); } @@ -144,8 +153,11 @@ public function testAuthenticateWithUserProvider(): void $request = self::createRequest(values: $values); $request->setSession(new Session(new MockArraySessionStorage())); - $actual = $authenticator->authenticate($request); - self::assertInstanceOf(Passport::class, $actual); + $passport = $authenticator->authenticate($request); + self::assertHasBadge($passport, UserBadge::class); + self::assertHasBadge($passport, RememberMeBadge::class); + self::assertHasBadge($passport, PasswordUpgradeBadge::class); + self::assertHasBadge($passport, CsrfTokenBadge::class); } /** @@ -235,7 +247,8 @@ public function testGetLoginUrl(): void ->willReturn($expected); $authenticator = $this->createAuthenticator(httpUtils: $httpUtils); - $actual = $authenticator->onAuthenticationFailure(new Request(), new AuthenticationException()); + $request = self::createRequest(); + $actual = $authenticator->onAuthenticationFailure($request, new AuthenticationException()); self::assertInstanceOf(RedirectResponse::class, $actual); self::assertSame($expected, $actual->getTargetUrl()); } @@ -250,7 +263,7 @@ public function testOnAuthenticationSuccess(): void ->willReturn(true); $authenticator = $this->createAuthenticator(httpUtils: $httpUtils); - $request = new Request(); + $request = self::createRequest(); $token = $this->createMock(TokenInterface::class); $actual = $authenticator->onAuthenticationSuccess($request, $token, 'fake'); self::assertNull($actual); @@ -271,6 +284,15 @@ public function testSupports(Request $request, bool $expected): void self::assertSame($expected, $actual); } + /** + * @param class-string $badgeClass + */ + protected static function assertHasBadge(Passport $passport, string $badgeClass): void + { + $actual = $passport->getBadge($badgeClass); + self::assertInstanceOf($badgeClass, $actual); + } + /** * @throws Exception */ diff --git a/tests/Service/ApplicationServiceTest.php b/tests/Service/ApplicationServiceTest.php index c1ea650dd..38accb53d 100644 --- a/tests/Service/ApplicationServiceTest.php +++ b/tests/Service/ApplicationServiceTest.php @@ -29,7 +29,6 @@ use App\Tests\DatabaseTrait; use App\Tests\DateAssertTrait; use App\Tests\KernelServiceTestCase; -use App\Tests\TranslatorMockTrait; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Exception\ORMException; use PHPUnit\Framework\MockObject\Exception; @@ -41,7 +40,6 @@ class ApplicationServiceTest extends KernelServiceTestCase { use DatabaseTrait; use DateAssertTrait; - use TranslatorMockTrait; public function testActions(): void { diff --git a/tests/Service/OpenWeatherServiceTest.php b/tests/Service/OpenWeatherServiceTest.php index a2d044afe..dbeb3328c 100644 --- a/tests/Service/OpenWeatherServiceTest.php +++ b/tests/Service/OpenWeatherServiceTest.php @@ -15,12 +15,9 @@ use App\Service\OpenWeatherService; use App\Tests\KernelServiceTestCase; -use App\Tests\TranslatorMockTrait; class OpenWeatherServiceTest extends KernelServiceTestCase { - use TranslatorMockTrait; - private const CITY_INVALID = 0; private const CITY_VALID = 2_660_718; diff --git a/translations/security.fr_CH.yaml b/translations/security.fr_CH.yaml index e3f58b4a8..169dfab20 100644 --- a/translations/security.fr_CH.yaml +++ b/translations/security.fr_CH.yaml @@ -38,3 +38,8 @@ reset_not_found_password_token: Aucun jeton de ré-initialisation du mot de pass reset_user_not_found: Impossible de retrouver l'utilisateur %name% dans la base de données. reset_token_not_found: Impossible de générer un jeton de ré-initialisation pour l'utilisateur %name%. reset_token_send: Le lien de réinitialisation du mot de passe a été envoyé avec succès à l'utilisateur %name%. +# login form authenticator +authenticator: + empty_user: Le nom de l'utilisateur est requis. + empty_password: Le mot de passe est requis. + empty_token: Le jeton de falsification de requête inter-sites (CSRF) est requis. diff --git a/vendor-bin/psalm/composer.lock b/vendor-bin/psalm/composer.lock index d6b3888f5..24587322d 100644 --- a/vendor-bin/psalm/composer.lock +++ b/vendor-bin/psalm/composer.lock @@ -504,29 +504,27 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "1.4.10 || 2.0.3", + "phpstan/phpstan-phpunit": "^1.0 || ^2", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" + "psr/log": "^1 || ^2 || ^3" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -534,7 +532,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + "Doctrine\\Deprecations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -545,9 +543,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + "source": "https://github.com/doctrine/deprecations/tree/1.1.4" }, - "time": "2024-01-30T19:34:25+00:00" + "time": "2024-12-07T21:18:45+00:00" }, { "name": "felixfbecker/advanced-json-rpc",