From 6197b44364ffa357cb12aebb63bdd3f8d88cf3c9 Mon Sep 17 00:00:00 2001 From: Leandro Silva Date: Tue, 22 Sep 2015 08:03:54 -0300 Subject: [PATCH 01/19] ReCaptcha api migrated to v2 --- src/ReCaptcha.php | 262 ++++++++++++++++++++--------------------- src/Response.php | 71 +++++------ test/ReCaptchaTest.php | 120 +++++++++---------- test/ResponseTest.php | 50 ++++---- 4 files changed, 248 insertions(+), 255 deletions(-) diff --git a/src/ReCaptcha.php b/src/ReCaptcha.php index 42b9028..bc71021 100644 --- a/src/ReCaptcha.php +++ b/src/ReCaptcha.php @@ -3,7 +3,6 @@ * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ - namespace ZendService\ReCaptcha; use Exception as PhpException; @@ -17,40 +16,34 @@ */ class ReCaptcha { - /** - * URI to the regular API - * - * @var string - */ - const API_SERVER = 'http://www.google.com/recaptcha/api'; /** - * URI to the secure API + * URI to the API * * @var string */ - const API_SECURE_SERVER = 'https://www.google.com/recaptcha/api'; + const API_SERVER = 'http://www.google.com/recaptcha/api'; /** * URI to the verify server * * @var string */ - const VERIFY_SERVER = 'http://www.google.com/recaptcha/api/verify'; + const VERIFY_SERVER = 'http://www.google.com/recaptcha/api/siteverify'; /** - * Public key used when displaying the captcha + * Site key used when displaying the captcha * * @var string */ - protected $publicKey = null; + protected $siteKey = null; /** - * Private key used when verifying user input + * Secret key used when verifying user input * * @var string */ - protected $privateKey = null; + protected $secretKey = null; /** * Ip address used when verifying user input @@ -64,61 +57,56 @@ class ReCaptcha * * @var array */ - protected $params = [ - 'ssl' => false, // Use SSL or not when generating the recaptcha - 'error' => null, // The error message to display in the recaptcha - 'xhtml' => false // Enable XHTML output (this will not be XHTML Strict - // compliant since the IFRAME is necessary when - // Javascript is disabled) - ]; + protected $params = array( + 'noscript' => false, /* Includes the HTML; - + } return $return; } /** - * Post a solution to the verify server + * Gets a solution to the verify server * - * @param string $challengeField * @param string $responseField * @return \Zend\Http\Response - * @throws Exception + * @throws \ZendService\ReCaptcha\Exception */ - protected function post($challengeField, $responseField) + protected function post($responseField) { - if ($this->privateKey === null) { - throw new Exception('Missing private key'); + if ($this->secretKey === null) { + throw new Exception('Missing secret key'); } if ($this->ip === null) { @@ -456,14 +450,15 @@ protected function post($challengeField, $responseField) /* Fetch an instance of the http client */ $httpClient = $this->getHttpClient(); - $postParams = ['privatekey' => $this->privateKey, - 'remoteip' => $this->ip, - 'challenge' => $challengeField, - 'response' => $responseField]; + $params = [ + 'secret' => $this->secretKey, + 'remoteip' => $this->ip, + 'response' => $responseField + ]; - $request = new HttpRequest; + $request = new HttpRequest(); $request->setUri(self::VERIFY_SERVER); - $request->getPost()->fromArray($postParams); + $request->getPost()->fromArray($params); $request->setMethod(HttpRequest::METHOD_POST); $httpClient->setEncType($httpClient::ENC_URLENCODED); @@ -474,15 +469,14 @@ protected function post($challengeField, $responseField) * Verify the user input * * This method calls up the post method and returns a - * Zend_Service_ReCaptcha_Response object. + * \ZendService\ReCaptcha\Response object. * - * @param string $challengeField * @param string $responseField - * @return Response + * @return \ZendService\ReCaptcha\Response */ - public function verify($challengeField, $responseField) + public function verify($responseField) { - $response = $this->post($challengeField, $responseField); + $response = $this->post($responseField); return new Response(null, null, $response); } } diff --git a/src/Response.php b/src/Response.php index 548848f..dda37ec 100644 --- a/src/Response.php +++ b/src/Response.php @@ -23,30 +23,30 @@ class Response protected $status = null; /** - * Error code + * Error codes * - * The error code if the status is false. The different error codes can be found in the + * The error codes if the status is false. The different error codes can be found in the * recaptcha API docs. * - * @var string + * @var array */ - protected $errorCode = null; + protected $errorCodes = []; /** * Class constructor used to construct a response * * @param string $status - * @param string $errorCode - * @param HTTPResponse $httpResponse If this is set the content will override $status and $errorCode + * @param array $errorCodes + * @param \Zend\Http\Response $httpResponse If this is set the content will override $status and $errorCode */ - public function __construct($status = null, $errorCode = null, HTTPResponse $httpResponse = null) + public function __construct($status = null, $errorCodes = [], HTTPResponse $httpResponse = null) { if ($status !== null) { $this->setStatus($status); } - if ($errorCode !== null) { - $this->setErrorCode($errorCode); + if (!empty($errorCodes)) { + $this->setErrorCodes($errorCodes); } if ($httpResponse !== null) { @@ -57,16 +57,12 @@ public function __construct($status = null, $errorCode = null, HTTPResponse $htt /** * Set the status * - * @param string $status - * @return Response + * @param boolean $status + * @return \ZendService\ReCaptcha\Response */ public function setStatus($status) { - if ($status === 'true') { - $this->status = true; - } else { - $this->status = false; - } + $this->status = (bool) $status; return $this; } @@ -92,49 +88,56 @@ public function isValid() } /** - * Set the error code + * Set the error codes * - * @param string $errorCode - * @return Response + * @param array $errorCodes + * @return \ZendService\ReCaptcha\Response */ - public function setErrorCode($errorCode) + public function setErrorCodes($errorCodes) { - $this->errorCode = $errorCode; + if (is_string($errorCodes)) { + $errorCodes = [$errorCodes]; + } + + $this->errorCodes = $errorCodes; return $this; } /** - * Get the error code + * Get the error codes * - * @return string + * @return array */ - public function getErrorCode() + public function getErrorCodes() { - return $this->errorCode; + return $this->errorCodes; } /** * Populate this instance based on a Zend_Http_Response object * - * @param HTTPResponse $response - * @return Response + * @param \Zend\Http\Response $response + * @return \ZendService\ReCaptcha\Response */ public function setFromHttpResponse(HTTPResponse $response) { $body = $response->getBody(); - $parts = explode("\n", $body, 2); + $parts = json_decode($body, true); + + $status = false; + $errorCodes = []; - if (count($parts) !== 2) { - $status = 'false'; - $errorCode = ''; - } else { - list($status, $errorCode) = $parts; + if (is_array($parts) && array_key_exists('success', $parts)) { + $status = $parts['success']; + if (array_key_exists('error-codes', $parts)) { + $errorCodes = $parts['error-codes']; + } } $this->setStatus($status); - $this->setErrorCode($errorCode); + $this->setErrorCodes($errorCodes); return $this; } diff --git a/test/ReCaptchaTest.php b/test/ReCaptchaTest.php index 3615468..9230113 100644 --- a/test/ReCaptchaTest.php +++ b/test/ReCaptchaTest.php @@ -20,13 +20,13 @@ class ReCaptchaTest extends TestCase public function setUp() { - $this->publicKey = getenv('TESTS_ZEND_SERVICE_RECAPTCHA_PUBLIC_KEY'); - $this->privateKey = getenv('TESTS_ZEND_SERVICE_RECAPTCHA_PRIVATE_KEY'); + $this->siteKey = getenv('TESTS_ZEND_SERVICE_RECAPTCHA_SITE_KEY'); + $this->secretKey = getenv('TESTS_ZEND_SERVICE_RECAPTCHA_SECRET_KEY'); - if (empty($this->publicKey) - || $this->publicKey == 'public key' - || empty($this->privateKey) - || $this->privateKey == 'private key' + if (empty($this->siteKey) + || $this->siteKey == 'site key' + || empty($this->secretKey) + || $this->secretKey == 'secret key' ) { $this->markTestSkipped('ZendService\ReCaptcha\ReCaptcha tests skipped due to missing keys'); } @@ -41,13 +41,13 @@ public function testSetAndGet() $this->reCaptcha->setIp($ip); $this->assertSame($ip, $this->reCaptcha->getIp()); - /* Set and get public key */ - $this->reCaptcha->setPublicKey($this->publicKey); - $this->assertSame($this->publicKey, $this->reCaptcha->getPublicKey()); + /* Set and get site key */ + $this->reCaptcha->setSiteKey($this->siteKey); + $this->assertSame($this->siteKey, $this->reCaptcha->getSiteKey()); - /* Set and get private key */ - $this->reCaptcha->setPrivateKey($this->privateKey); - $this->assertSame($this->privateKey, $this->reCaptcha->getPrivateKey()); + /* Set and get secret key */ + $this->reCaptcha->setSecretKey($this->secretKey); + $this->assertSame($this->secretKey, $this->reCaptcha->getSecretKey()); } public function testSingleParam() @@ -68,22 +68,18 @@ public function testMultipleParams() { $params = [ 'ssl' => true, - 'error' => 'errorMsg', - 'xhtml' => true, ]; $this->reCaptcha->setParams($params); $_params = $this->reCaptcha->getParams(); $this->assertSame($params['ssl'], $_params['ssl']); - $this->assertSame($params['error'], $_params['error']); - $this->assertSame($params['xhtml'], $_params['xhtml']); } public function testSingleOption() { $key = 'theme'; - $value = 'black'; + $value = 'dark'; $this->reCaptcha->setOption($key, $value); $this->assertSame($value, $this->reCaptcha->getOption($key)); @@ -97,23 +93,21 @@ public function tetsGetNonExistingOption() public function testMultipleOptions() { $options = [ - 'theme' => 'black', - 'lang' => 'no', + 'theme' => 'dark', + 'hl' => 'en', ]; $this->reCaptcha->setOptions($options); $_options = $this->reCaptcha->getOptions(); $this->assertSame($options['theme'], $_options['theme']); - $this->assertSame($options['lang'], $_options['lang']); + $this->assertSame($options['hl'], $_options['hl']); } public function testSetMultipleParamsFromZendConfig() { $params = [ 'ssl' => true, - 'error' => 'errorMsg', - 'xhtml' => true, ]; $config = new Config\Config($params); @@ -122,8 +116,6 @@ public function testSetMultipleParamsFromZendConfig() $_params = $this->reCaptcha->getParams(); $this->assertSame($params['ssl'], $_params['ssl']); - $this->assertSame($params['error'], $_params['error']); - $this->assertSame($params['xhtml'], $_params['xhtml']); } public function testSetInvalidParams() @@ -136,8 +128,8 @@ public function testSetInvalidParams() public function testSetMultipleOptionsFromZendConfig() { $options = [ - 'theme' => 'black', - 'lang' => 'no', + 'theme' => 'dark', + 'hl' => 'en', ]; $config = new Config\Config($options); @@ -146,7 +138,7 @@ public function testSetMultipleOptionsFromZendConfig() $_options = $this->reCaptcha->getOptions(); $this->assertSame($options['theme'], $_options['theme']); - $this->assertSame($options['lang'], $_options['lang']); + $this->assertSame($options['hl'], $_options['hl']); } public function testSetInvalidOptions() @@ -159,30 +151,26 @@ public function testSetInvalidOptions() public function testConstructor() { $params = [ - 'ssl' => true, - 'error' => 'errorMsg', - 'xhtml' => true, + 'noscript' => true, ]; $options = [ - 'theme' => 'black', - 'lang' => 'no', + 'theme' => 'dark', + 'hl' => 'en', ]; $ip = '127.0.0.1'; - $reCaptcha = new ReCaptcha($this->publicKey, $this->privateKey, $params, $options, $ip); + $reCaptcha = new ReCaptcha($this->siteKey, $this->secretKey, $params, $options, $ip); $_params = $reCaptcha->getParams(); $_options = $reCaptcha->getOptions(); - $this->assertSame($this->publicKey, $reCaptcha->getPublicKey()); - $this->assertSame($this->privateKey, $reCaptcha->getPrivateKey()); - $this->assertSame($params['ssl'], $_params['ssl']); - $this->assertSame($params['error'], $_params['error']); - $this->assertSame($params['xhtml'], $_params['xhtml']); + $this->assertSame($this->siteKey, $reCaptcha->getSiteKey()); + $this->assertSame($this->secretKey, $reCaptcha->getSecretKey()); + $this->assertSame($params['noscript'], $_params['noscript']); $this->assertSame($options['theme'], $_options['theme']); - $this->assertSame($options['lang'], $_options['lang']); + $this->assertSame($options['hl'], $_options['hl']); $this->assertSame($ip, $reCaptcha->getIp()); } @@ -202,13 +190,13 @@ public function testGetHtmlWithNoPublicKey() { $this->setExpectedException('ZendService\\ReCaptcha\\Exception'); - $html = $this->reCaptcha->getHtml(); + $this->reCaptcha->getHtml(); } public function testVerify() { - $this->reCaptcha->setPublicKey($this->publicKey); - $this->reCaptcha->setPrivateKey($this->privateKey); + $this->reCaptcha->setSiteKey($this->siteKey); + $this->reCaptcha->setSecretKey($this->secretKey); $this->reCaptcha->setIp('127.0.0.1'); $adapter = new \Zend\Http\Client\Adapter\Test(); @@ -227,42 +215,40 @@ public function testVerify() public function testGetHtml() { - $this->reCaptcha->setPublicKey($this->publicKey); - $errorMsg = 'errorMsg'; - $this->reCaptcha->setParam('ssl', true); - $this->reCaptcha->setParam('xhtml', true); - $this->reCaptcha->setParam('error', $errorMsg); + $this->reCaptcha->setSiteKey($this->siteKey); $html = $this->reCaptcha->getHtml(); // See if the options for the captcha exist in the string - $this->assertNotSame(false, strstr($html, 'var RecaptchaOptions = {"theme":"red","lang":"en"};')); + $this->assertNotSame(false, strstr($html, sprintf('data-sitekey="%s"', $this->siteKey))); // See if the js/iframe src is correct $this->assertNotSame( - false, - strstr( - $html, - sprintf( - 'src="%s/challenge?k=%s&error=%s"', - ReCaptcha::API_SECURE_SERVER, - $this->publicKey, - $errorMsg - ) - ) + true, + strstr($html,'reCaptcha->setSiteKey($this->siteKey); + $this->reCaptcha->setOption('hl', 'en'); + + $html = $this->reCaptcha->getHtml(); + + $this->assertContains('?hl=en', $html); + } + /** @group ZF-10991 */ - public function testHtmlGenerationWillUseSuppliedNameForNoScriptElements() + public function testHtmlGenerationWithNoScriptElements() { - $this->reCaptcha->setPublicKey($this->publicKey); - $html = $this->reCaptcha->getHtml('contact'); - $this->assertContains('contact[recaptcha_challenge_field]', $html); - $this->assertContains('contact[recaptcha_response_field]', $html); + $this->reCaptcha->setSiteKey($this->siteKey); + $this->reCaptcha->setParam('noscript', true); + $html = $this->reCaptcha->getHtml(); + $this->assertContains('setExpectedException('ZendService\\ReCaptcha\\Exception'); @@ -273,13 +259,13 @@ public function testVerifyWithMissingIp() { $this->setExpectedException('ZendService\\ReCaptcha\\Exception'); - $this->reCaptcha->setPrivateKey($this->privateKey); + $this->reCaptcha->setSecretKey($this->secretKey); $this->reCaptcha->verify('challenge', 'response'); } public function testVerifyWithMissingChallengeField() { - $this->reCaptcha->setPrivateKey($this->privateKey); + $this->reCaptcha->setSecretKey($this->secretKey); $this->reCaptcha->setIp('127.0.0.1'); $response = $this->reCaptcha->verify('', 'response'); $this->assertFalse($response->getStatus()); @@ -287,7 +273,7 @@ public function testVerifyWithMissingChallengeField() public function testVerifyWithMissingResponseField() { - $this->reCaptcha->setPrivateKey($this->privateKey); + $this->reCaptcha->setSecretKey($this->secretKey); $this->reCaptcha->setIp('127.0.0.1'); $response = $this->reCaptcha->verify('challenge', ''); $this->assertFalse($response->getStatus()); diff --git a/test/ResponseTest.php b/test/ResponseTest.php index 9463610..8741138 100644 --- a/test/ResponseTest.php +++ b/test/ResponseTest.php @@ -22,37 +22,44 @@ public function setUp() public function testSetAndGet() { /* Set and get status */ - $status = 'true'; + $status = true; $this->response->setStatus($status); $this->assertSame(true, $this->response->getStatus()); - $status = 'false'; + $status = false; $this->response->setStatus($status); $this->assertSame(false, $this->response->getStatus()); - /* Set and get the error code */ - $errorCode = 'foobar'; - $this->response->setErrorCode($errorCode); - $this->assertSame($errorCode, $this->response->getErrorCode()); + /* Set and get the error codes */ + $errorCodes = 'foobar'; + $this->response->setErrorCodes($errorCodes); + $this->assertSame([$errorCodes], $this->response->getErrorCodes()); + + $errorCodes = ['foo', 'bar']; + $this->response->setErrorCodes($errorCodes); + $this->assertSame($errorCodes, $this->response->getErrorCodes()); } public function testIsValid() { - $this->response->setStatus('true'); + $this->response->setStatus(true); $this->assertSame(true, $this->response->isValid()); } public function testIsInvalid() { - $this->response->setStatus('false'); + $this->response->setStatus(false); $this->assertSame(false, $this->response->isValid()); } public function testSetFromHttpResponse() { - $status = 'false'; - $errorCode = 'foobar'; - $responseBody = $status . "\n" . $errorCode; + $status = false; + $errorCodes = ['foo', 'bar']; + $responseBody = json_encode([ + 'success' => $status, + 'error-codes' => $errorCodes + ]); $httpResponse = new Response(); $httpResponse->setStatusCode(200); $httpResponse->getHeaders()->addHeaderLine('Content-Type', 'text/html'); @@ -61,25 +68,28 @@ public function testSetFromHttpResponse() $this->response->setFromHttpResponse($httpResponse); $this->assertSame(false, $this->response->getStatus()); - $this->assertSame($errorCode, $this->response->getErrorCode()); + $this->assertSame($errorCodes, $this->response->getErrorCodes()); } public function testConstructor() { - $status = 'true'; - $errorCode = 'ok'; + $status = true; + $errorCodes = ['ok']; - $response = new ReCaptcha\Response($status, $errorCode); + $response = new ReCaptcha\Response($status, $errorCodes); $this->assertSame(true, $response->getStatus()); - $this->assertSame($errorCode, $response->getErrorCode()); + $this->assertSame($errorCodes, $response->getErrorCodes()); } public function testConstructorWithHttpResponse() { - $status = 'false'; - $errorCode = 'foobar'; - $responseBody = $status . "\n" . $errorCode; + $status = false; + $errorCodes = ['foobar']; + $responseBody = json_encode([ + 'success' => $status, + 'error-codes' => $errorCodes + ]); $httpResponse = new Response(); $httpResponse->setStatusCode(200); $httpResponse->getHeaders()->addHeaderLine('Content-Type', 'text/html'); @@ -88,6 +98,6 @@ public function testConstructorWithHttpResponse() $response = new ReCaptcha\Response(null, null, $httpResponse); $this->assertSame(false, $response->getStatus()); - $this->assertSame($errorCode, $response->getErrorCode()); + $this->assertSame($errorCodes, $response->getErrorCodes()); } } From 17af428fde0b06585c705d79f6ce0dd56db0ab31 Mon Sep 17 00:00:00 2001 From: Leandro Silva Date: Tue, 22 Sep 2015 08:05:42 -0300 Subject: [PATCH 02/19] Making composer instructions compatible with previous call --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a609bc8..bdc6c80 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ You can install using: ``` curl -s https://getcomposer.org/installer | php php composer.phar install -composer require zendframework/zendservice-recaptcha +php composer.phar require zendframework/zendservice-recaptcha ``` At that point, follow the instructions in the documentation folder for actual From 2d3b86187fcd319d256bf644a76114943bbed40c Mon Sep 17 00:00:00 2001 From: Leandro Silva Date: Tue, 22 Sep 2015 08:15:12 -0300 Subject: [PATCH 03/19] CS fixes --- src/ReCaptcha.php | 4 ++-- test/ReCaptchaTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ReCaptcha.php b/src/ReCaptcha.php index bc71021..1fc244c 100644 --- a/src/ReCaptcha.php +++ b/src/ReCaptcha.php @@ -57,9 +57,9 @@ class ReCaptcha * * @var array */ - protected $params = array( + protected $params = [ 'noscript' => false, /* Includes the