diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2cf50fb..f954f79 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,13 +14,21 @@ jobs: strategy: fail-fast: true matrix: - php: [7.1, 7.2, 7.3, 7.4, 8.0] - symfony: [2.8, 3.4, 4.4, 5.2] + php: [7.1, 7.2, 7.3, 7.4, 8.0, 8.1] + symfony: [2.8, 3.4, 4.4, 5.2, 6.0] exclude: - php: 7.1 symfony: 5.2 - php: 8.0 symfony: 3.4 + - php: 7.1 + symfony: 6.0 + - php: 7.2 + symfony: 6.0 + - php: 7.3 + symfony: 6.0 + - php: 7.4 + symfony: 6.0 steps: - name: Checkout uses: actions/checkout@v2 diff --git a/composer.json b/composer.json index 8017aad..a45a77e 100644 --- a/composer.json +++ b/composer.json @@ -17,15 +17,15 @@ "require": { "php": "^7.1 || ^8.0", "google/recaptcha": "^1.1", - "symfony/form": "^2.8 || ^3.0 || ^4.0 || ^5.0", - "symfony/framework-bundle": "^2.8 || ^3.0 || ^4.0 || ^5.0", - "symfony/security-bundle": "^2.8 || ^3.0 || ^4.0 || ^5.0", - "symfony/validator": "^2.8 || ^3.0 || ^4.0 || ^5.0", - "symfony/yaml": "^2.8 || ^3.0 || ^4.0 || ^5.0", + "symfony/form": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0", + "symfony/framework-bundle": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0", + "symfony/security-bundle": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0", + "symfony/validator": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0", + "symfony/yaml": "^2.8 || ^3.0 || ^4.0 || ^5.0 || ^6.0", "twig/twig": "^1.40 || ^2.9 || ^3.0" }, "require-dev": { - "phpunit/phpunit": "^7 || ^8" + "phpunit/phpunit": "^7 || ^8 || ^9.5" }, "autoload": { "psr-4": { diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index bfe3dfb..f5ecb03 100755 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -14,7 +14,7 @@ class Configuration implements ConfigurationInterface /** * {@inheritdoc} */ - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('ewz_recaptcha'); diff --git a/src/DependencyInjection/EWZRecaptchaExtension.php b/src/DependencyInjection/EWZRecaptchaExtension.php index 615e722..c1489ba 100755 --- a/src/DependencyInjection/EWZRecaptchaExtension.php +++ b/src/DependencyInjection/EWZRecaptchaExtension.php @@ -19,7 +19,7 @@ class EWZRecaptchaExtension extends Extension /** * {@inheritdoc} */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); @@ -38,7 +38,7 @@ public function load(array $configs, ContainerBuilder $container) $recaptchaService->replaceArgument(1, new Reference('ewz_recaptcha.extension.recaptcha.request_method.proxy_post')); } - if (3 == $config['version']) { + if (3 === $config['version']) { $container->register('ewz_recaptcha.form_builder_factory', EWZRecaptchaV3FormBuilderFactory::class) ->addArgument(new Reference('form.factory')); } else { @@ -61,13 +61,13 @@ public function load(array $configs, ContainerBuilder $container) * * @param ContainerBuilder $container */ - protected function registerWidget(ContainerBuilder $container, $version = 2) + protected function registerWidget(ContainerBuilder $container, int $version = 2): void { $templatingEngines = $container->hasParameter('templating.engines') ? $container->getParameter('templating.engines') : array('twig'); - if (in_array('php', $templatingEngines)) { + if (in_array('php', $templatingEngines, true)) { $formResource = 'EWZRecaptchaBundle:Form'; $container->setParameter('templating.helper.form.resources', array_merge( @@ -76,7 +76,7 @@ protected function registerWidget(ContainerBuilder $container, $version = 2) )); } - if (in_array('twig', $templatingEngines)) { + if (in_array('twig', $templatingEngines, true)) { $formResource = '@EWZRecaptcha/Form/ewz_recaptcha_widget.html.twig'; if (3 === $version) { $formResource = '@EWZRecaptcha/Form/v3/ewz_recaptcha_widget.html.twig'; diff --git a/src/Extension/ReCaptcha/RequestMethod/Post.php b/src/Extension/ReCaptcha/RequestMethod/Post.php index 1d40e5e..dae3aa5 100644 --- a/src/Extension/ReCaptcha/RequestMethod/Post.php +++ b/src/Extension/ReCaptcha/RequestMethod/Post.php @@ -33,7 +33,7 @@ class Post implements RequestMethod * @param string $recaptchaVerifyServer * @param int|null $timeout */ - public function __construct($recaptchaVerifyServer, $timeout) + public function __construct(string $recaptchaVerifyServer, ?int $timeout) { $this->recaptchaVerifyUrl = ($recaptchaVerifyServer ?: 'https://www.google.com').'/recaptcha/api/siteverify'; $this->timeout = $timeout; @@ -47,7 +47,7 @@ public function __construct($recaptchaVerifyServer, $timeout) * * @return string Body of the reCAPTCHA response */ - public function submit(RequestParameters $params) + public function submit(RequestParameters $params): string { $cacheKey = $params->toQueryString(); if (isset($this->cache[$cacheKey])) { diff --git a/src/Extension/ReCaptcha/RequestMethod/ProxyPost.php b/src/Extension/ReCaptcha/RequestMethod/ProxyPost.php index 4352105..9c6013a 100644 --- a/src/Extension/ReCaptcha/RequestMethod/ProxyPost.php +++ b/src/Extension/ReCaptcha/RequestMethod/ProxyPost.php @@ -41,7 +41,7 @@ class ProxyPost implements RequestMethod * @param string $recaptchaVerifyServer * @param int|null $timeout */ - public function __construct(array $httpProxy, $recaptchaVerifyServer, $timeout) + public function __construct(array $httpProxy, string $recaptchaVerifyServer, ?int $timeout) { $this->httpProxy = $httpProxy; $this->recaptchaVerifyUrl = ($recaptchaVerifyServer ?: 'https://www.google.com').'/recaptcha/api/siteverify'; @@ -56,7 +56,7 @@ public function __construct(array $httpProxy, $recaptchaVerifyServer, $timeout) * * @return string Body of the reCAPTCHA response */ - public function submit(RequestParameters $params) + public function submit(RequestParameters $params): string { $cacheKey = $params->toQueryString(); if (isset($this->cache[$cacheKey])) { diff --git a/src/Form/Type/AbstractEWZRecaptchaType.php b/src/Form/Type/AbstractEWZRecaptchaType.php index db30945..c0ef48e 100755 --- a/src/Form/Type/AbstractEWZRecaptchaType.php +++ b/src/Form/Type/AbstractEWZRecaptchaType.php @@ -38,10 +38,10 @@ abstract class AbstractEWZRecaptchaType extends AbstractType /** * @param string $publicKey Recaptcha public key - * @param bool $enabled Recaptcha status + * @param bool $enabled Recaptcha status * @param string $apiHost Api host */ - public function __construct($publicKey, $enabled, $apiHost = 'www.google.com') + public function __construct(string $publicKey, bool $enabled, string $apiHost = 'www.google.com') { $this->publicKey = $publicKey; $this->enabled = $enabled; @@ -52,7 +52,7 @@ public function __construct($publicKey, $enabled, $apiHost = 'www.google.com') /** * {@inheritdoc} */ - public function buildView(FormView $view, FormInterface $form, array $options) + public function buildView(FormView $view, FormInterface $form, array $options): void { $view->vars = array_replace($view->vars, array( 'ewz_recaptcha_enabled' => $this->enabled, @@ -71,7 +71,7 @@ public function buildView(FormView $view, FormInterface $form, array $options) /** * {@inheritdoc} */ - public function getBlockPrefix() + public function getBlockPrefix(): string { return 'ewz_recaptcha'; } @@ -81,7 +81,7 @@ public function getBlockPrefix() * * @return string The javascript source URL */ - public function getPublicKey() + public function getPublicKey(): string { return $this->publicKey; } @@ -91,10 +91,10 @@ public function getPublicKey() * * @return string The hostname for API */ - public function getApiHost() + public function getApiHost(): string { return $this->apiHost; } - abstract protected function addCustomVars(FormView $view, FormInterface $form, array $options); + abstract protected function addCustomVars(FormView $view, FormInterface $form, array $options): void; } diff --git a/src/Form/Type/EWZRecaptchaType.php b/src/Form/Type/EWZRecaptchaType.php index bfb4101..b5672d8 100755 --- a/src/Form/Type/EWZRecaptchaType.php +++ b/src/Form/Type/EWZRecaptchaType.php @@ -29,7 +29,7 @@ class EWZRecaptchaType extends AbstractEWZRecaptchaType * @param bool $ajax Ajax status * @param LocaleResolver $localeResolver */ - public function __construct($publicKey, $enabled, $ajax, LocaleResolver $localeResolver, $apiHost = 'www.google.com') + public function __construct(string $publicKey, bool $enabled, bool $ajax, LocaleResolver $localeResolver, string $apiHost = 'www.google.com') { parent::__construct($publicKey, $enabled, $apiHost); @@ -40,7 +40,7 @@ public function __construct($publicKey, $enabled, $ajax, LocaleResolver $localeR /** * {@inheritdoc} */ - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults(array( 'compound' => false, @@ -67,7 +67,7 @@ public function configureOptions(OptionsResolver $resolver) /** * {@inheritdoc} */ - public function getParent() + public function getParent(): string { return TextType::class; } @@ -87,7 +87,7 @@ public function getScriptURL($key) /** * {@inheritdoc} */ - protected function addCustomVars(FormView $view, FormInterface $form, array $options) + protected function addCustomVars(FormView $view, FormInterface $form, array $options): void { $view->vars = array_replace($view->vars, array( 'ewz_recaptcha_ajax' => $this->ajax, diff --git a/src/Form/Type/EWZRecaptchaV3Type.php b/src/Form/Type/EWZRecaptchaV3Type.php index 39f109e..989a728 100755 --- a/src/Form/Type/EWZRecaptchaV3Type.php +++ b/src/Form/Type/EWZRecaptchaV3Type.php @@ -18,11 +18,11 @@ class EWZRecaptchaV3Type extends AbstractEWZRecaptchaType * EWZRecaptchaV3Type constructor. * * @param string $publicKey - * @param bool $enabled + * @param bool $enabled * @param bool $hideBadge * @param string $apiHost */ - public function __construct($publicKey, $enabled, $hideBadge, $apiHost = 'www.google.com') + public function __construct(string $publicKey, bool $enabled, bool $hideBadge, string $apiHost = 'www.google.com') { parent::__construct($publicKey, $enabled, $apiHost); @@ -32,7 +32,7 @@ public function __construct($publicKey, $enabled, $hideBadge, $apiHost = 'www.go /** * {@inheritdoc} */ - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'label' => false, @@ -49,7 +49,7 @@ public function configureOptions(OptionsResolver $resolver) /** * {@inheritdoc} */ - public function getParent() + public function getParent(): string { return HiddenType::class; } @@ -57,12 +57,12 @@ public function getParent() /** * {@inheritdoc} */ - protected function addCustomVars(FormView $view, FormInterface $form, array $options) + protected function addCustomVars(FormView $view, FormInterface $form, array $options): void { $view->vars = array_replace($view->vars, [ 'ewz_recaptcha_hide_badge' => $this->hideBadge, - 'script_nonce_csp' => isset($options['script_nonce_csp']) ? $options['script_nonce_csp'] : '', - 'action_name' => isset($options['action_name']) ? $options['action_name'] : '', + 'script_nonce_csp' => $options['script_nonce_csp'] ?? '', + 'action_name' => $options['action_name'] ?? '', ]); } } diff --git a/src/Locale/LocaleResolver.php b/src/Locale/LocaleResolver.php index 1a2c929..ede7097 100644 --- a/src/Locale/LocaleResolver.php +++ b/src/Locale/LocaleResolver.php @@ -23,7 +23,7 @@ final class LocaleResolver * @param bool $useLocaleFromRequest * @param RequestStack $requestStack */ - public function __construct($defaultLocale, $useLocaleFromRequest, RequestStack $requestStack) + public function __construct(string $defaultLocale, bool $useLocaleFromRequest, RequestStack $requestStack) { $this->defaultLocale = $defaultLocale; $this->useLocaleFromRequest = $useLocaleFromRequest; @@ -33,7 +33,7 @@ public function __construct($defaultLocale, $useLocaleFromRequest, RequestStack /** * @return string The resolved locale key, depending on configuration */ - public function resolve() + public function resolve(): string { return $this->useLocaleFromRequest ? $this->requestStack->getCurrentRequest()->getLocale() diff --git a/src/Validator/Constraints/IsTrue.php b/src/Validator/Constraints/IsTrue.php index 8207d0a..a45510b 100644 --- a/src/Validator/Constraints/IsTrue.php +++ b/src/Validator/Constraints/IsTrue.php @@ -25,7 +25,7 @@ public function getTargets() /** * {@inheritdoc} */ - public function validatedBy() + public function validatedBy(): string { return 'ewz_recaptcha.true'; } diff --git a/src/Validator/Constraints/IsTrueV3.php b/src/Validator/Constraints/IsTrueV3.php index a48e778..ff38428 100755 --- a/src/Validator/Constraints/IsTrueV3.php +++ b/src/Validator/Constraints/IsTrueV3.php @@ -11,7 +11,7 @@ class IsTrueV3 extends IsTrue /** * {@inheritdoc} */ - public function validatedBy() + public function validatedBy(): string { return 'ewz_recaptcha.v3.true'; } diff --git a/src/Validator/Constraints/IsTrueValidator.php b/src/Validator/Constraints/IsTrueValidator.php index 416702c..963329a 100755 --- a/src/Validator/Constraints/IsTrueValidator.php +++ b/src/Validator/Constraints/IsTrueValidator.php @@ -62,11 +62,11 @@ class IsTrueValidator extends ConstraintValidator * @param array $trustedRoles */ public function __construct( - $enabled, + bool $enabled, ReCaptcha $recaptcha, RequestStack $requestStack, - $verifyHost, - AuthorizationCheckerInterface $authorizationChecker = null, + bool $verifyHost, + ?AuthorizationCheckerInterface $authorizationChecker = null, array $trustedRoles = array()) { $this->enabled = $enabled; @@ -80,7 +80,7 @@ public function __construct( /** * {@inheritdoc} */ - public function validate($value, Constraint $constraint) + public function validate($value, Constraint $constraint): void { // if recaptcha is disabled, always valid if (!$this->enabled) { diff --git a/src/Validator/Constraints/IsTrueValidatorV3.php b/src/Validator/Constraints/IsTrueValidatorV3.php index a095433..05f6c78 100755 --- a/src/Validator/Constraints/IsTrueValidatorV3.php +++ b/src/Validator/Constraints/IsTrueValidatorV3.php @@ -55,7 +55,7 @@ public function __construct( * @param mixed $value * @param Constraint $constraint */ - public function validate($value, Constraint $constraint) + public function validate($value, Constraint $constraint): void { if (!$this->enabled) { return; @@ -85,7 +85,7 @@ public function validate($value, Constraint $constraint) * * @return bool */ - private function isTokenValid($token) + private function isTokenValid(string $token): bool { try { $remoteIp = $this->requestStack->getCurrentRequest()->getClientIp(); @@ -99,7 +99,7 @@ private function isTokenValid($token) ->verify($token, $remoteIp); return $response->isSuccess(); - } catch (\Exception $exception) { + } catch (\Throwable $exception) { $this->logger->error( 'reCAPTCHA validator error: '.$exception->getMessage(), [ diff --git a/tests/DependencyInjection/EWZRecaptchaExtensionTest.php b/tests/DependencyInjection/EWZRecaptchaExtensionTest.php index 049153f..b8be3ae 100755 --- a/tests/DependencyInjection/EWZRecaptchaExtensionTest.php +++ b/tests/DependencyInjection/EWZRecaptchaExtensionTest.php @@ -21,7 +21,7 @@ protected function tearDown(): void $this->configuration = null; } - public function testSimpleConfiguration() + public function testSimpleConfiguration(): void { $this->configuration = new ContainerBuilder(); $loader = new EWZRecaptchaExtension(); @@ -57,7 +57,7 @@ public function testSimpleConfiguration() ); } - public function testSimpleV3Configuration() + public function testSimpleV3Configuration(): void { $this->configuration = new ContainerBuilder(); $loader = new EWZRecaptchaExtension(); @@ -74,7 +74,7 @@ public function testSimpleV3Configuration() } - public function testFullConfiguration() + public function testFullConfiguration(): void { $this->configuration = new ContainerBuilder(); $loader = new EWZRecaptchaExtension(); @@ -145,22 +145,22 @@ private function getFullConfig() return $parser->parse($yaml); } - private function assertParameter($value, $key) + private function assertParameter($value, $key): void { $this->assertSame($value, $this->configuration->getParameter($key), sprintf('%s parameter is correct', $key)); } - private function assertHasDefinition($id) + private function assertHasDefinition($id): void { $this->assertTrue(($this->configuration->hasDefinition($id) ?: $this->configuration->hasAlias($id))); } - private function assertNotHasDefinition($id) + private function assertNotHasDefinition($id): void { $this->assertFalse(($this->configuration->hasDefinition($id) ?: $this->configuration->hasAlias($id))); } - private function assertDefinitionHasReferenceArgument($id, $index, $expectedArgumentValue) + private function assertDefinitionHasReferenceArgument($id, $index, $expectedArgumentValue): void { $definition = $this->configuration->getDefinition($id); $argumentValue = $definition->getArgument($index); diff --git a/tests/Form/Type/EWZRecaptchaTypeTest.php b/tests/Form/Type/EWZRecaptchaTypeTest.php index 894158b..30f4f2a 100644 --- a/tests/Form/Type/EWZRecaptchaTypeTest.php +++ b/tests/Form/Type/EWZRecaptchaTypeTest.php @@ -26,7 +26,7 @@ protected function setUp(): void /** * @test */ - public function buildView() + public function buildView(): void { $view = new FormView(); @@ -45,7 +45,7 @@ public function buildView() /** * @test */ - public function getParent() + public function getParent(): void { $this->assertSame(TextType::class, $this->type->getParent()); } @@ -53,7 +53,7 @@ public function getParent() /** * @test */ - public function getPublicKey() + public function getPublicKey(): void { $this->assertSame('key', $this->type->getPublicKey()); } @@ -61,7 +61,7 @@ public function getPublicKey() /** * @test */ - public function configureOptions() + public function configureOptions(): void { $optionsResolver = new OptionsResolver(); @@ -96,7 +96,7 @@ public function configureOptions() /** * @test */ - public function getBlockPrefix() + public function getBlockPrefix(): void { $this->assertEquals('ewz_recaptcha', $this->type->getBlockPrefix()); } diff --git a/tests/Form/Type/EWZRecaptchaV3TypeTest.php b/tests/Form/Type/EWZRecaptchaV3TypeTest.php index 8355d9b..20ac500 100755 --- a/tests/Form/Type/EWZRecaptchaV3TypeTest.php +++ b/tests/Form/Type/EWZRecaptchaV3TypeTest.php @@ -25,7 +25,7 @@ protected function setUp(): void /** * @test */ - public function buildView() + public function buildView(): void { $view = new FormView(); @@ -44,7 +44,7 @@ public function buildView() /** * @test */ - public function getParent() + public function getParent(): void { $this->assertSame(HiddenType::class, $this->type->getParent()); } @@ -52,7 +52,7 @@ public function getParent() /** * @test */ - public function getPublicKey() + public function getPublicKey(): void { $this->assertSame('key', $this->type->getPublicKey()); } @@ -60,7 +60,7 @@ public function getPublicKey() /** * @test */ - public function configureOptions() + public function configureOptions(): void { $optionsResolver = new OptionsResolver(); @@ -82,7 +82,7 @@ public function configureOptions() /** * @test */ - public function getBlockPrefix() + public function getBlockPrefix(): void { $this->assertEquals('ewz_recaptcha', $this->type->getBlockPrefix()); } diff --git a/tests/Locale/LocaleResolverTest.php b/tests/Locale/LocaleResolverTest.php index a8bbcf7..341c5bf 100644 --- a/tests/Locale/LocaleResolverTest.php +++ b/tests/Locale/LocaleResolverTest.php @@ -12,10 +12,11 @@ class LocaleResolverTest extends TestCase /** * @test */ - public function resolveWithLocaleFromRequest() + public function resolveWithLocaleFromRequest(): void { + $locale = 'locale'; $request = $this->createMock(Request::class); - $request->expects($this->once())->method('getLocale'); + $request->expects($this->once())->method('getLocale')->willReturn($locale); $requestStack = $this->createMock(RequestStack::class); $requestStack @@ -24,13 +25,13 @@ public function resolveWithLocaleFromRequest() ->willReturn($request); $resolver = new LocaleResolver('foo', true, $requestStack); - $resolver->resolve(); + self::assertSame($locale, $resolver->resolve()); } /** * @test */ - public function resolveWithDefaultLocale() + public function resolveWithDefaultLocale(): void { $requestStack = $this->createMock(RequestStack::class); $requestStack diff --git a/tests/Validator/Constraints/IsTrueValidatorTest.php b/tests/Validator/Constraints/IsTrueValidatorTest.php new file mode 100644 index 0000000..38fd86e --- /dev/null +++ b/tests/Validator/Constraints/IsTrueValidatorTest.php @@ -0,0 +1,308 @@ +createMock(ReCaptcha::class); + $reCaptcha->expects(self::never()) + ->method('verify'); + $requestStack = $this->createMock(RequestStack::class); + $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + $context = $this->createMock(ExecutionContextInterface::class); + $context->expects(self::never()) + ->method('addViolation'); + $context->expects(self::never()) + ->method('buildViolation'); + + $authorizationChecker->expects(self::never()) + ->method('isGranted'); + + $validator = new IsTrueValidator(false, $reCaptcha, $requestStack, true, $authorizationChecker, []); + $validator->initialize($context); + $validator->validate('', new IsTrue()); + } + + public function testTrustedRolesAreNotValidated(): void + { + $trustedRoles = ['ROLE_TEST']; + $reCaptcha = $this->createMock(ReCaptcha::class); + $reCaptcha->expects(self::never()) + ->method('verify'); + $requestStack = $this->createMock(RequestStack::class); + $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + $context = $this->createMock(ExecutionContextInterface::class); + $context->expects(self::never()) + ->method('addViolation'); + $context->expects(self::never()) + ->method('buildViolation'); + + $authorizationChecker->expects(self::once()) + ->method('isGranted') + ->with($trustedRoles) + ->willReturn(true); + + if (\is_callable([$requestStack, 'getMainRequest'])) { + $requestStack->expects(self::never()) + ->method('getMainRequest'); + } else { + $requestStack->expects(self::never()) + ->method('getMasterRequest'); + } + + $validator = new IsTrueValidator(true, $reCaptcha, $requestStack, true, $authorizationChecker, $trustedRoles); + $validator->validate('', new IsTrue()); + } + + public function testResponseNotSuccess(): void + { + $trustedRoles = ['ROLE_TEST']; + $clientIp = '127.0.0.1'; + $recaptchaAnswer = 'encoded response'; + $constraint = new IsTrue(); + $reCaptcha = $this->createMock(ReCaptcha::class); + $requestStack = $this->createMock(RequestStack::class); + $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + $context = $this->createMock(ExecutionContextInterface::class); + $context->expects(self::never()) + ->method('buildViolation'); + + $authorizationChecker->expects(self::once()) + ->method('isGranted') + ->with($trustedRoles) + ->willReturn(false); + + $request = $this->createMock(Request::class); + $request->expects(self::once()) + ->method('getClientIp') + ->willReturn($clientIp); + $request->expects(self::once()) + ->method('get') + ->with('g-recaptcha-response') + ->willReturn($recaptchaAnswer); + + if (\is_callable([$requestStack, 'getMainRequest'])) { + $requestStack->expects(self::once()) + ->method('getMainRequest') + ->willReturn($request); + } else { + $requestStack->expects(self::once()) + ->method('getMasterRequest') + ->willReturn($request); + } + + $response = $this->createMock(Response::class); + $response->expects(self::once()) + ->method('isSuccess') + ->willReturn(false); + + $reCaptcha->expects(self::once()) + ->method('verify') + ->with($recaptchaAnswer, $clientIp) + ->willReturn($response); + + $context->expects(self::once()) + ->method('addViolation') + ->with($constraint->message); + + $validator = new IsTrueValidator(true, $reCaptcha, $requestStack, true, $authorizationChecker, $trustedRoles); + $validator->initialize($context); + $validator->validate('', $constraint); + } + + public function testInvalidHostWithVerifyHost(): void + { + $trustedRoles = ['ROLE_TEST']; + $clientIp = '127.0.0.1'; + $recaptchaAnswer = 'encoded response'; + $constraint = new IsTrue(); + $reCaptcha = $this->createMock(ReCaptcha::class); + $requestStack = $this->createMock(RequestStack::class); + $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + $context = $this->createMock(ExecutionContextInterface::class); + $context->expects(self::never()) + ->method('buildViolation'); + + $authorizationChecker->expects(self::once()) + ->method('isGranted') + ->with($trustedRoles) + ->willReturn(false); + + $request = $this->createMock(Request::class); + $request->expects(self::once()) + ->method('getClientIp') + ->willReturn($clientIp); + $request->expects(self::once()) + ->method('get') + ->with('g-recaptcha-response') + ->willReturn($recaptchaAnswer); + $request->expects(self::once()) + ->method('getHost') + ->willReturn('host1'); + + if (\is_callable([$requestStack, 'getMainRequest'])) { + $requestStack->expects(self::once()) + ->method('getMainRequest') + ->willReturn($request); + } else { + $requestStack->expects(self::once()) + ->method('getMasterRequest') + ->willReturn($request); + } + + $response = $this->createMock(Response::class); + $response->expects(self::once()) + ->method('isSuccess') + ->willReturn(true); + $response->expects(self::once()) + ->method('getHostname') + ->willReturn('host2'); + + $reCaptcha->expects(self::once()) + ->method('verify') + ->with($recaptchaAnswer, $clientIp) + ->willReturn($response); + + $context->expects(self::once()) + ->method('addViolation') + ->with($constraint->invalidHostMessage); + + $validator = new IsTrueValidator(true, $reCaptcha, $requestStack, true, $authorizationChecker, $trustedRoles); + $validator->initialize($context); + $validator->validate('', $constraint); + } + + public function testInvalidHostWithoutVerifyHost(): void + { + $trustedRoles = ['ROLE_TEST']; + $clientIp = '127.0.0.1'; + $recaptchaAnswer = 'encoded response'; + $constraint = new IsTrue(); + $reCaptcha = $this->createMock(ReCaptcha::class); + $requestStack = $this->createMock(RequestStack::class); + $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + $context = $this->createMock(ExecutionContextInterface::class); + $context->expects(self::never()) + ->method('buildViolation'); + + $authorizationChecker->expects(self::once()) + ->method('isGranted') + ->with($trustedRoles) + ->willReturn(false); + + $request = $this->createMock(Request::class); + $request->expects(self::once()) + ->method('getClientIp') + ->willReturn($clientIp); + $request->expects(self::once()) + ->method('get') + ->with('g-recaptcha-response') + ->willReturn($recaptchaAnswer); + $request->expects(self::never()) + ->method('getHost'); + + if (\is_callable([$requestStack, 'getMainRequest'])) { + $requestStack->expects(self::once()) + ->method('getMainRequest') + ->willReturn($request); + } else { + $requestStack->expects(self::once()) + ->method('getMasterRequest') + ->willReturn($request); + } + + $response = $this->createMock(Response::class); + $response->expects(self::once()) + ->method('isSuccess') + ->willReturn(true); + $response->expects(self::never()) + ->method('getHostname'); + + $reCaptcha->expects(self::once()) + ->method('verify') + ->with($recaptchaAnswer, $clientIp) + ->willReturn($response); + + $context->expects(self::never()) + ->method('addViolation'); + + $validator = new IsTrueValidator(true, $reCaptcha, $requestStack, false, $authorizationChecker, $trustedRoles); + $validator->initialize($context); + $validator->validate('', $constraint); + } + + public function testValidWithVerifyHost(): void + { + $trustedRoles = ['ROLE_TEST']; + $clientIp = '127.0.0.1'; + $recaptchaAnswer = 'encoded response'; + $host = 'host'; + $constraint = new IsTrue(); + $reCaptcha = $this->createMock(ReCaptcha::class); + $requestStack = $this->createMock(RequestStack::class); + $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + $context = $this->createMock(ExecutionContextInterface::class); + $context->expects(self::never()) + ->method('buildViolation'); + $context->expects(self::never()) + ->method('addViolation'); + + $authorizationChecker->expects(self::once()) + ->method('isGranted') + ->with($trustedRoles) + ->willReturn(false); + + $request = $this->createMock(Request::class); + $request->expects(self::once()) + ->method('getClientIp') + ->willReturn($clientIp); + $request->expects(self::once()) + ->method('get') + ->with('g-recaptcha-response') + ->willReturn($recaptchaAnswer); + $request->expects(self::once()) + ->method('getHost') + ->willReturn($host); + + if (\is_callable([$requestStack, 'getMainRequest'])) { + $requestStack->expects(self::once()) + ->method('getMainRequest') + ->willReturn($request); + } else { + $requestStack->expects(self::once()) + ->method('getMasterRequest') + ->willReturn($request); + } + + $response = $this->createMock(Response::class); + $response->expects(self::once()) + ->method('isSuccess') + ->willReturn(true); + $response->expects(self::once()) + ->method('getHostname') + ->willReturn($host); + + $reCaptcha->expects(self::once()) + ->method('verify') + ->with($recaptchaAnswer, $clientIp) + ->willReturn($response); + + $validator = new IsTrueValidator(true, $reCaptcha, $requestStack, true, $authorizationChecker, $trustedRoles); + $validator->initialize($context); + $validator->validate('', $constraint); + } + +} diff --git a/tests/Validator/Constraints/IsTrueValidatorV3Test.php b/tests/Validator/Constraints/IsTrueValidatorV3Test.php new file mode 100644 index 0000000..b4b8ad3 --- /dev/null +++ b/tests/Validator/Constraints/IsTrueValidatorV3Test.php @@ -0,0 +1,70 @@ +createMock(RequestStack::class); + $logger = $this->createMock(LoggerInterface::class); + $context = $this->createMock(ExecutionContextInterface::class); + $context->expects(self::never()) + ->method('addViolation'); + $context->expects(self::never()) + ->method('buildViolation'); + + $validator = new IsTrueValidatorV3(false, 'secret', 0.1, $requestStack, $logger); + $validator->initialize($context); + $validator->validate('', $this->createMock(Constraint::class)); + } + + public function testRequiresV3(): void + { + $requestStack = $this->createMock(RequestStack::class); + $logger = $this->createMock(LoggerInterface::class); + $context = $this->createMock(ExecutionContextInterface::class); + $context->expects(self::never()) + ->method('addViolation'); + $context->expects(self::never()) + ->method('buildViolation'); + + $this->expectException(UnexpectedTypeException::class); + $this->expectExceptionMessage('Expected argument of type "EWZ\Bundle\RecaptchaBundle\Validator\Constraints\IsTrueV3",'); + + $validator = new IsTrueValidatorV3(true, 'secret', 0.1, $requestStack, $logger); + $validator->initialize($context); + $validator->validate('', $this->createMock(IsTrue::class)); + } + + public function testRequiresValueNotNullButNotString(): void + { + $requestStack = $this->createMock(RequestStack::class); + $logger = $this->createMock(LoggerInterface::class); + $context = $this->createMock(ExecutionContextInterface::class); + $context->expects(self::never()) + ->method('addViolation'); + $context->expects(self::never()) + ->method('buildViolation'); + + $this->expectException(UnexpectedTypeException::class); + $this->expectExceptionMessage('Expected argument of type "string", "stdClass" given'); + + $validator = new IsTrueValidatorV3(true, 'secret', 0.1, $requestStack, $logger); + $validator->initialize($context); + $validator->validate(new stdClass(), new IsTrueV3()); + } + +}