Skip to content

Commit

Permalink
Merge pull request #245 from pulzarraider/cache_response
Browse files Browse the repository at this point in the history
Enable caching recaptcha verify response in array cache
  • Loading branch information
excelwebzone authored Jul 15, 2020
2 parents 21fea6d + cfd42a4 commit 1b3c8ca
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 56 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"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",
"twig/twig": "^1.40 || ^2.9 || ^3.0"
},
"require-dev": {
Expand Down
6 changes: 6 additions & 0 deletions src/DependencyInjection/EWZRecaptchaExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;

Expand All @@ -28,6 +29,11 @@ public function load(array $configs, ContainerBuilder $container)
}

$this->registerWidget($container);

if (null !== $config['http_proxy']['host'] && null !== $config['http_proxy']['port']) {
$recaptchaService = $container->findDefinition('ewz_recaptcha.recaptcha');
$recaptchaService->replaceArgument(1, new Reference('ewz_recaptcha.extension.recaptcha.request_method.proxy_post'));
}
}

/**
Expand Down
23 changes: 20 additions & 3 deletions src/Extension/ReCaptcha/RequestMethod/Post.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,37 @@ class Post implements RequestMethod
private $timeout;

/**
* Constructor
* @var array
*/
private $cache;

/**
* Constructor.
*
* @param string $recaptchaVerifyServer
* @param string $recaptchaVerifyServer
* @param int|null $timeout
*/
public function __construct($recaptchaVerifyServer, $timeout)
{
$this->recaptchaVerifyUrl = ($recaptchaVerifyServer ?: 'https://www.google.com').'/recaptcha/api/siteverify';
$this->timeout = $timeout;
$this->cache = [];
}

/**
* Submit the POST request with the specified parameters.
*
* @param RequestParameters $params Request parameters
*
* @return string Body of the reCAPTCHA response
*/
public function submit(RequestParameters $params)
{
$cacheKey = $params->toQueryString();
if (isset($this->cache[$cacheKey])) {
return $this->cache[$cacheKey];
}

/**
* PHP 5.6.0 changed the way you specify the peer name for SSL context options.
* Using "CN_name" will still work, but it will raise deprecated errors.
Expand All @@ -63,6 +76,10 @@ public function submit(RequestParameters $params)
$options['http']['timeout'] = $this->timeout;
}
$context = stream_context_create($options);
return file_get_contents($this->recaptchaVerifyUrl, false, $context);
$result = file_get_contents($this->recaptchaVerifyUrl, false, $context);

$this->cache[$cacheKey] = $result;

return $result;
}
}
28 changes: 23 additions & 5 deletions src/Extension/ReCaptcha/RequestMethod/ProxyPost.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,45 @@ class ProxyPost implements RequestMethod
*/
private $timeout;

/**
* @var array
*/
private $cache;

/**
* Constructor
*
* @param array $httpProxy proxy data to connect to
* @param string $recaptchaVerifyServer
* @param array $httpProxy
* @param string $recaptchaVerifyServer
* @param int|null $timeout
*/
public function __construct(array $httpProxy, $recaptchaVerifyServer, $timeout)
{
$this->httpProxy = $httpProxy;
$this->recaptchaVerifyUrl = ($recaptchaVerifyServer ?: 'https://www.google.com').'/recaptcha/api/siteverify';
$this->timeout = $timeout;
$this->cache = [];
}

/**
* Submit the POST request with the specified parameters.
*
* @param RequestParameters $params Request parameters
*
* @return string Body of the reCAPTCHA response
*/
public function submit(RequestParameters $params)
{
$cacheKey = $params->toQueryString();
if (isset($this->cache[$cacheKey])) {
return $this->cache[$cacheKey];
}

/**
* PHP 5.6.0 changed the way you specify the peer name for SSL context options.
* Using "CN_name" will still work, but it will raise deprecated errors.
*/
$peer_key = version_compare(PHP_VERSION, '5.6.0', '<') ? 'CN_name' : 'peer_name';
$peerKey = version_compare(PHP_VERSION, '5.6.0', '<') ? 'CN_name' : 'peer_name';
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n".sprintf('Proxy-Authorization: Basic %s', base64_encode($this->httpProxy['auth'])),
Expand All @@ -65,7 +78,7 @@ public function submit(RequestParameters $params)
// Force the peer to validate (not needed in 5.6.0+, but still works)
'verify_peer' => true,
// Force the peer validation to use www.google.com
$peer_key => 'www.google.com',
$peerKey => 'www.google.com',

'proxy' => sprintf('tcp://%s:%s', $this->httpProxy['host'], $this->httpProxy['port']),
// While this is a non-standard request format, some proxy servers require it.
Expand All @@ -76,6 +89,11 @@ public function submit(RequestParameters $params)
$options['http']['timeout'] = $this->timeout;
}
$context = stream_context_create($options);
return file_get_contents($this->recaptchaVerifyUrl, false, $context);

$result = file_get_contents($this->recaptchaVerifyUrl, false, $context);

$this->cache[$cacheKey] = $result;

return $result;
}
}
27 changes: 23 additions & 4 deletions src/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,32 @@ services:
public: true
arguments:
- '%ewz_recaptcha.enabled%'
- '%ewz_recaptcha.private_key%'
- '@ewz_recaptcha.recaptcha'
- '@request_stack'
- '%ewz_recaptcha.http_proxy%'
- '%ewz_recaptcha.verify_host%'
- '@?security.authorization_checker'
- '%ewz_recaptcha.trusted_roles%'
- '%ewz_recaptcha.api_host%'
- '%ewz_recaptcha.timeout%'
tags:
- { name: validator.constraint_validator, alias: 'ewz_recaptcha.true' }

ewz_recaptcha.recaptcha:
class: ReCaptcha\ReCaptcha
public: false
arguments:
- '%ewz_recaptcha.private_key%'
- '@ewz_recaptcha.extension.recaptcha.request_method.post'

ewz_recaptcha.extension.recaptcha.request_method.post:
class: EWZ\Bundle\RecaptchaBundle\Extension\ReCaptcha\RequestMethod\Post
public: false
arguments:
- 'https://%ewz_recaptcha.api_host%'
- '%ewz_recaptcha.timeout%'

ewz_recaptcha.extension.recaptcha.request_method.proxy_post:
class: EWZ\Bundle\RecaptchaBundle\Extension\ReCaptcha\RequestMethod\ProxyPost
public: false
arguments:
- '%ewz_recaptcha.http_proxy%'
- 'https://%ewz_recaptcha.api_host%'
- '%ewz_recaptcha.timeout%'
52 changes: 8 additions & 44 deletions src/Validator/Constraints/IsTrueValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ class IsTrueValidator extends ConstraintValidator
protected $enabled;

/**
* Recaptcha Private Key.
* Recaptcha.
*
* @var string
* @var ReCaptcha
*/
protected $privateKey;
protected $recaptcha;

/**
* Request Stack.
Expand All @@ -34,13 +34,6 @@ class IsTrueValidator extends ConstraintValidator
*/
protected $requestStack;

/**
* HTTP Proxy informations.
*
* @var array
*/
protected $httpProxy;

/**
* Enable serverside host check.
*
Expand All @@ -62,51 +55,28 @@ class IsTrueValidator extends ConstraintValidator
*/
protected $trustedRoles;

/**
* The reCAPTCHA verify server URL.
*
* @var string
*/
protected $recaptchaVerifyServer;

/**
* The timeout for the reCAPTCHA verification.
*
* @var int|null
*/
private $timeout;

/**
* @param bool $enabled
* @param string $privateKey
* @param ReCaptcha $recaptcha
* @param RequestStack $requestStack
* @param array $httpProxy
* @param bool $verifyHost
* @param AuthorizationCheckerInterface|null $authorizationChecker
* @param array $trustedRoles
* @param string $apiHost
* @param int|null $timeout
*/
public function __construct(
$enabled,
$privateKey,
ReCaptcha $recaptcha,
RequestStack $requestStack,
array $httpProxy,
$verifyHost,
AuthorizationCheckerInterface $authorizationChecker = null,
array $trustedRoles = array(),
$apiHost = 'www.google.com',
$timeout = null)
array $trustedRoles = array())
{
$this->enabled = $enabled;
$this->privateKey = $privateKey;
$this->recaptcha = $recaptcha;
$this->requestStack = $requestStack;
$this->httpProxy = $httpProxy;
$this->verifyHost = $verifyHost;
$this->authorizationChecker = $authorizationChecker;
$this->trustedRoles = $trustedRoles;
$this->recaptchaVerifyServer = 'https://'.$apiHost;
$this->timeout = $timeout;
}

/**
Expand All @@ -132,13 +102,7 @@ public function validate($value, Constraint $constraint)
$answer = $masterRequest->get('g-recaptcha-response');

// Verify user response with Google
if (null !== $this->httpProxy['host'] && null !== $this->httpProxy['port']) {
$requestMethod = new ProxyPost($this->httpProxy, $this->recaptchaVerifyServer, $this->timeout);
} else {
$requestMethod = new Post($this->recaptchaVerifyServer, $this->timeout);
}
$recaptcha = new ReCaptcha($this->privateKey, $requestMethod);
$response = $recaptcha->verify($answer, $remoteip);
$response = $this->recaptcha->verify($answer, $remoteip);

if (!$response->isSuccess()) {
$this->context->addViolation($constraint->message);
Expand Down
Loading

0 comments on commit 1b3c8ca

Please sign in to comment.