diff --git a/api/components/ReCaptcha/Validator.php b/api/components/ReCaptcha/Validator.php index bacb2dc..1dfac33 100644 --- a/api/components/ReCaptcha/Validator.php +++ b/api/components/ReCaptcha/Validator.php @@ -1,7 +1,8 @@ getComponent() === null) { + throw new InvalidConfigException('Required "reCaptcha" component as instance of ' . Component::class . '.'); + } + + $this->when = function() { + return !YII_ENV_TEST; + }; + } + + /** + * @inheritdoc + */ + protected function validateValue($value) { + if (empty($value)) { + return [$this->requiredMessage, []]; + } + + $response = $this->createClient()->post(self::SITE_VERIFY_URL, [ + 'form_params' => [ + 'secret' => $this->getComponent()->secret, + 'response' => $value, + 'remoteip' => Yii::$app->getRequest()->getUserIP(), + ], + ]); + $data = json_decode($response->getBody(), true); + + if (!isset($data['success'])) { + throw new Exception('Invalid recaptcha verify response.'); + } + + return $data['success'] ? null : [$this->message, []]; + } + /** * @return Component */ @@ -20,46 +59,8 @@ class Validator extends \yii\validators\Validator { return Yii::$app->reCaptcha; } - public function init() { - parent::init(); - if ($this->getComponent() === null) { - throw new InvalidConfigException('Required "reCaptcha" component as instance of ' . Component::class . '.'); - } - - if ($this->message === null) { - $this->message = Yii::t('yii', 'The verification code is incorrect.'); - } - } - - /** - * @inheritdoc - */ - protected function validateValue($value) { - $value = Yii::$app->request->post(self::CAPTCHA_RESPONSE_FIELD); - if (empty($value)) { - return [$this->message, []]; - } - - $requestParams = [ - 'secret' => $this->getComponent()->secret, - 'response' => $value, - 'remoteip' => Yii::$app->request->userIP, - ]; - - $requestUrl = self::SITE_VERIFY_URL . '?' . http_build_query($requestParams); - $response = $this->getResponse($requestUrl); - - if (!isset($response['success'])) { - throw new Exception('Invalid recaptcha verify response.'); - } - - return $response['success'] ? null : [$this->message, []]; - } - - protected function getResponse($request) { - $response = file_get_contents($request); - - return json_decode($response, true); + protected function createClient() { + return new GuzzleClient(); } } diff --git a/common/helpers/Error.php b/common/helpers/Error.php index e398b9a..b14b944 100644 --- a/common/helpers/Error.php +++ b/common/helpers/Error.php @@ -40,6 +40,7 @@ final class Error { const REFRESH_TOKEN_REQUIRED = 'error.refresh_token_required'; const REFRESH_TOKEN_NOT_EXISTS = 'error.refresh_token_not_exist'; + const CAPTCHA_REQUIRED = 'error.captcha_required'; const CAPTCHA_INVALID = 'error.captcha_invalid'; const RULES_AGREEMENT_REQUIRED = 'error.rulesAgreement_required'; diff --git a/tests/codeception/api/unit/components/ReCaptcha/ValidatorTest.php b/tests/codeception/api/unit/components/ReCaptcha/ValidatorTest.php new file mode 100644 index 0000000..54ce065 --- /dev/null +++ b/tests/codeception/api/unit/components/ReCaptcha/ValidatorTest.php @@ -0,0 +1,65 @@ +specify('Get error.captcha_required, if passed empty value', function() { + $validator = new Validator(); + expect($validator->validate('', $error))->false(); + expect($error)->equals('error.captcha_required'); + }); + + $this->specify('Get error.captcha_invalid, if passed wrong value', function() { + /** @var \PHPUnit_Framework_MockObject_MockObject|Validator $validator */ + $validator = $this->getMockBuilder(Validator::class) + ->setMethods(['createClient']) + ->getMock(); + + $validator->expects($this->once()) + ->method('createClient') + ->will($this->returnValue($this->createMockGuzzleClient([ + 'success' => false, + 'error-codes' => [ + 'invalid-input-response', // The response parameter is invalid or malformed. + ], + ]))); + + expect($validator->validate('12341234', $error))->false(); + expect($error)->equals('error.captcha_invalid'); + }); + + $this->specify('Get error.captcha_invalid, if passed wrong value', function() { + /** @var \PHPUnit_Framework_MockObject_MockObject|Validator $validator */ + $validator = $this->getMockBuilder(Validator::class) + ->setMethods(['createClient']) + ->getMock(); + + $validator->expects($this->once()) + ->method('createClient') + ->will($this->returnValue($this->createMockGuzzleClient(['success' => true]))); + + expect($validator->validate('12341234', $error))->true(); + expect($error)->null(); + }); + } + + private function createMockGuzzleClient(array $response) { + $mock = new MockHandler([ + new Response(200, [], json_encode($response)), + ]); + $handler = HandlerStack::create($mock); + + return new Client(['handler' => $handler]); + } + +} diff --git a/tests/codeception/config/api/config.php b/tests/codeception/config/api/config.php index 564ecf0..86797ca 100644 --- a/tests/codeception/config/api/config.php +++ b/tests/codeception/config/api/config.php @@ -4,5 +4,9 @@ return [ 'user' => [ 'secret' => 'tests-secret-key', ], + 'reCaptcha' => [ + 'public' => 'public-key', + 'secret' => 'private-key', + ], ], ];