Last active
October 21, 2018 05:41
-
-
Save gheydon/4afc0517dc6ab1f60f1ec2466ddd6081 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 33be1afb8103775be9e0db47ace822b42e6ed747 Mon Sep 17 00:00:00 2001 | |
From: Gordon Heydon <[email protected]> | |
Date: Sun, 21 Oct 2018 16:40:06 +1100 | |
Subject: [PATCH] patch | |
--- | |
composer.json | 2 +- | |
config/install/recaptcha.settings.yml | 2 + | |
config/schema/recaptcha.schema.yml | 6 + | |
js/recaptcha.js | 34 +++ | |
migration_templates/d6_recaptcha_settings.yml | 6 +- | |
migration_templates/d7_recaptcha_settings.yml | 8 +- | |
.../.github/ISSUE_TEMPLATE/bug_report.md | 28 +++ | |
recaptcha-php/.gitignore | 3 + | |
recaptcha-php/.travis.yml | 18 +- | |
recaptcha-php/ARCHITECTURE.md | 64 +++++ | |
recaptcha-php/CONTRIBUTING.md | 55 +++-- | |
recaptcha-php/README.md | 156 +++++++----- | |
recaptcha-php/app.yaml | 8 + | |
recaptcha-php/composer.json | 19 +- | |
recaptcha-php/examples/appengine-https.php | 33 +++ | |
recaptcha-php/examples/config.php.dist | 15 ++ | |
recaptcha-php/examples/example-captcha.php | 130 ---------- | |
recaptcha-php/examples/examples.css | 37 +++ | |
.../examples/google0afd8760fd68f119.html | 1 + | |
recaptcha-php/examples/index.php | 65 +++++ | |
.../recaptcha-v2-checkbox-explicit.php | 139 +++++++++++ | |
.../examples/recaptcha-v2-checkbox.php | 130 ++++++++++ | |
.../examples/recaptcha-v2-invisible.php | 132 ++++++++++ | |
.../examples/recaptcha-v3-request-scores.php | 109 ++++++++ | |
.../examples/recaptcha-v3-verify.php | 49 ++++ | |
recaptcha-php/examples/robots.txt | 2 + | |
recaptcha-php/phpunit.xml.dist | 3 + | |
recaptcha-php/src/ReCaptcha/ReCaptcha.php | 189 +++++++++++++- | |
recaptcha-php/src/ReCaptcha/RequestMethod.php | 2 +- | |
.../src/ReCaptcha/RequestMethod/Curl.php | 2 +- | |
.../src/ReCaptcha/RequestMethod/CurlPost.php | 38 +-- | |
.../src/ReCaptcha/RequestMethod/Post.php | 34 ++- | |
.../src/ReCaptcha/RequestMethod/Socket.php | 12 +- | |
.../ReCaptcha/RequestMethod/SocketPost.php | 47 +--- | |
.../src/ReCaptcha/RequestParameters.php | 6 +- | |
recaptcha-php/src/ReCaptcha/Response.php | 106 +++++++- | |
recaptcha-php/src/autoload.php | 10 +- | |
.../tests/ReCaptcha/ReCaptchaTest.php | 139 ++++++++++- | |
.../ReCaptcha/RequestMethod/CurlPostTest.php | 62 ++++- | |
.../ReCaptcha/RequestMethod/PostTest.php | 55 +++-- | |
.../RequestMethod/SocketPostTest.php | 56 ++++- | |
.../tests/ReCaptcha/RequestParametersTest.php | 7 +- | |
.../tests/ReCaptcha/ResponseTest.php | 113 +++++++-- | |
recaptcha.libraries.yml | 6 + | |
recaptcha.module | 61 ++++- | |
src/Form/ReCaptchaAdminSettingsForm.php | 22 +- | |
src/ReCaptcha/RequestMethod/Drupal8Post.php | 45 ++-- | |
src/Tests/ReCaptchaBasicTest.php | 178 ------------- | |
tests/src/Functional/ReCaptchaBasicTest.php | 233 ++++++++++++++++++ | |
49 files changed, 2082 insertions(+), 595 deletions(-) | |
create mode 100644 js/recaptcha.js | |
create mode 100644 recaptcha-php/.github/ISSUE_TEMPLATE/bug_report.md | |
create mode 100644 recaptcha-php/ARCHITECTURE.md | |
create mode 100644 recaptcha-php/app.yaml | |
create mode 100644 recaptcha-php/examples/appengine-https.php | |
create mode 100644 recaptcha-php/examples/config.php.dist | |
delete mode 100644 recaptcha-php/examples/example-captcha.php | |
create mode 100644 recaptcha-php/examples/examples.css | |
create mode 100644 recaptcha-php/examples/google0afd8760fd68f119.html | |
create mode 100644 recaptcha-php/examples/index.php | |
create mode 100644 recaptcha-php/examples/recaptcha-v2-checkbox-explicit.php | |
create mode 100644 recaptcha-php/examples/recaptcha-v2-checkbox.php | |
create mode 100644 recaptcha-php/examples/recaptcha-v2-invisible.php | |
create mode 100644 recaptcha-php/examples/recaptcha-v3-request-scores.php | |
create mode 100644 recaptcha-php/examples/recaptcha-v3-verify.php | |
create mode 100644 recaptcha-php/examples/robots.txt | |
create mode 100644 recaptcha.libraries.yml | |
delete mode 100644 src/Tests/ReCaptchaBasicTest.php | |
create mode 100644 tests/src/Functional/ReCaptchaBasicTest.php | |
diff --git a/composer.json b/composer.json | |
index 1d6aeaf..c4d75d3 100644 | |
--- a/composer.json | |
+++ b/composer.json | |
@@ -15,7 +15,7 @@ | |
], | |
"support": { | |
"issues": "https://www.drupal.org/project/issues/recaptcha", | |
- "source": "http://git.drupal.org/project/recaptcha.git" | |
+ "source": "https://git.drupal.org/project/recaptcha.git" | |
}, | |
"license": "GPL-2.0+", | |
"require": { | |
diff --git a/config/install/recaptcha.settings.yml b/config/install/recaptcha.settings.yml | |
index 00ec248..4591931 100644 | |
--- a/config/install/recaptcha.settings.yml | |
+++ b/config/install/recaptcha.settings.yml | |
@@ -1,5 +1,7 @@ | |
site_key: '' | |
secret_key: '' | |
+verify_hostname: false | |
+use_globally: false | |
widget: | |
theme: 'light' | |
type: 'image' | |
diff --git a/config/schema/recaptcha.schema.yml b/config/schema/recaptcha.schema.yml | |
index dd90863..dcc3b57 100644 | |
--- a/config/schema/recaptcha.schema.yml | |
+++ b/config/schema/recaptcha.schema.yml | |
@@ -10,6 +10,12 @@ recaptcha.settings: | |
secret_key: | |
type: string | |
label: 'Secret key' | |
+ verify_hostname: | |
+ type: boolean | |
+ label: 'Local domain name validation' | |
+ use_globally: | |
+ type: boolean | |
+ label: 'Use reCAPTCHA globally' | |
widget: | |
type: mapping | |
label: 'Widget settings' | |
diff --git a/js/recaptcha.js b/js/recaptcha.js | |
new file mode 100644 | |
index 0000000..a9a5912 | |
--- /dev/null | |
+++ b/js/recaptcha.js | |
@@ -0,0 +1,34 @@ | |
+/** | |
+ * @file | |
+ * Contains the definition of the behaviour recaptcha. | |
+ */ | |
+ | |
+(function ($, Drupal) { | |
+ Drupal.behaviors.recaptcha = { | |
+ attach: function (context) { | |
+ $('.g-recaptcha', context).each(function () { | |
+ if (typeof grecaptcha === 'undefined' || typeof grecaptcha.render !== 'function') { | |
+ return; | |
+ } | |
+ if ($(this).closest('body').length > 0) { | |
+ if ($(this).hasClass('recaptcha-processed')) { | |
+ grecaptcha.reset(); | |
+ } | |
+ else { | |
+ grecaptcha.render(this, $(this).data()); | |
+ $(this).addClass('recaptcha-processed'); | |
+ } | |
+ } | |
+ }); | |
+ } | |
+ }; | |
+ | |
+ window.drupalRecaptchaOnload = function () { | |
+ $('.g-recaptcha').each(function () { | |
+ if (!$(this).hasClass('recaptcha-processed')) { | |
+ grecaptcha.render(this, $(this).data()); | |
+ $(this).addClass('recaptcha-processed'); | |
+ } | |
+ }); | |
+ }; | |
+})(jQuery, Drupal); | |
diff --git a/migration_templates/d6_recaptcha_settings.yml b/migration_templates/d6_recaptcha_settings.yml | |
index bf6d859..67283bc 100644 | |
--- a/migration_templates/d6_recaptcha_settings.yml | |
+++ b/migration_templates/d6_recaptcha_settings.yml | |
@@ -6,13 +6,13 @@ migration_groups: | |
source: | |
plugin: variable | |
variables: | |
+ - recaptcha_noscript | |
- recaptcha_site_key | |
+ - recaptcha_size | |
- recaptcha_secret_key | |
+ - recaptcha_tabindex | |
- recaptcha_theme | |
- recaptcha_type | |
- - recaptcha_size | |
- - recaptcha_tabindex | |
- - recaptcha_noscript | |
source_module: recaptcha | |
process: | |
site_key: recaptcha_site_key | |
diff --git a/migration_templates/d7_recaptcha_settings.yml b/migration_templates/d7_recaptcha_settings.yml | |
index 5c48d9d..0bd295f 100644 | |
--- a/migration_templates/d7_recaptcha_settings.yml | |
+++ b/migration_templates/d7_recaptcha_settings.yml | |
@@ -6,17 +6,19 @@ migration_groups: | |
source: | |
plugin: variable | |
variables: | |
+ - recaptcha_noscript | |
- recaptcha_site_key | |
+ - recaptcha_size | |
- recaptcha_secret_key | |
+ - recaptcha_tabindex | |
- recaptcha_theme | |
- recaptcha_type | |
- - recaptcha_size | |
- - recaptcha_tabindex | |
- - recaptcha_noscript | |
+ - recaptcha_verify_hostname | |
source_module: recaptcha | |
process: | |
site_key: recaptcha_site_key | |
secret_key: recaptcha_secret_key | |
+ verify_hostname: recaptcha_verify_hostname | |
'widget/theme': recaptcha_theme | |
'widget/type': recaptcha_type | |
'widget/size': recaptcha_size | |
diff --git a/recaptcha-php/.github/ISSUE_TEMPLATE/bug_report.md b/recaptcha-php/.github/ISSUE_TEMPLATE/bug_report.md | |
new file mode 100644 | |
index 0000000..a14dcfe | |
--- /dev/null | |
+++ b/recaptcha-php/.github/ISSUE_TEMPLATE/bug_report.md | |
@@ -0,0 +1,28 @@ | |
+--- | |
+name: PHP client issue | |
+about: Report an issue with the PHP client library | |
+ | |
+--- | |
+ | |
+**Issue description** | |
+<!-- One or two sentences describing the problem --> | |
+ | |
+**Environment** | |
+<!-- The server or development environment where you're seeing the problem --> | |
+ | |
+ * OS name and version: | |
+ * PHP version: | |
+ * Web server name and version: | |
+ * `google/recaptcha` version: | |
+ * Browser name and version: | |
+ | |
+**Reproducing the issue** | |
+<!-- Where possible link to a URL where the problem can be seen or show code that causes it --> | |
+ | |
+ * URL (optional): <!-- if your integration is already deployed and the issue is visible --> | |
+ * Code (optional): <!-- share a link to the code you're using or an example in a Gist --> | |
+ | |
+ ***User steps*** | |
+ <!-- Detail the necessary steps to reproduce the issue. Include the output of any error messages. --> | |
+ | |
+ 1. Visit page... | |
diff --git a/recaptcha-php/.gitignore b/recaptcha-php/.gitignore | |
index 436384a..1c7edf1 100644 | |
--- a/recaptcha-php/.gitignore | |
+++ b/recaptcha-php/.gitignore | |
@@ -1,3 +1,6 @@ | |
+/.php_cs.cache | |
+/build | |
/composer.lock | |
+/examples/config.php | |
/nbproject/private/ | |
/vendor/ | |
diff --git a/recaptcha-php/.travis.yml b/recaptcha-php/.travis.yml | |
index a7da54d..13f9d7e 100644 | |
--- a/recaptcha-php/.travis.yml | |
+++ b/recaptcha-php/.travis.yml | |
@@ -1,3 +1,5 @@ | |
+dist: trusty | |
+ | |
language: php | |
sudo: false | |
@@ -7,8 +9,6 @@ php: | |
- '5.6' | |
- '7.0' | |
- '7.1' | |
- - hhvm | |
- - nightly | |
before_script: | |
- composer install | |
@@ -16,4 +16,16 @@ before_script: | |
- phpenv version-name | grep ^5.[34] && echo "apc.enable_cli=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini ; true | |
script: | |
- - vendor/bin/phpunit | |
+ - mkdir -p build/logs | |
+ - composer run-script lint | |
+ - composer run-script test | |
+ | |
+after_success: | |
+ - travis_retry php vendor/bin/php-coveralls | |
+ | |
+cache: | |
+ directories: | |
+ - "$HOME/.composer/cache/files" | |
+ | |
+git: | |
+ depth: 5 | |
diff --git a/recaptcha-php/ARCHITECTURE.md b/recaptcha-php/ARCHITECTURE.md | |
new file mode 100644 | |
index 0000000..13add26 | |
--- /dev/null | |
+++ b/recaptcha-php/ARCHITECTURE.md | |
@@ -0,0 +1,64 @@ | |
+# Architecture | |
+ | |
+The general pattern of usage is to instantiate the `ReCaptcha` class with your | |
+secret key, specify any additional validation rules, and then call `verify()` | |
+with the reCAPTCHA response and user's IP address. For example: | |
+ | |
+```php | |
+<?php | |
+$recaptcha = new \ReCaptcha\ReCaptcha($secret); | |
+$resp = $recaptcha->setExpectedHostname('recaptcha-demo.appspot.com') | |
+ ->verify($gRecaptchaResponse, $remoteIp); | |
+if ($resp->isSuccess()) { | |
+ // Verified! | |
+} else { | |
+ $errors = $resp->getErrorCodes(); | |
+} | |
+``` | |
+ | |
+By default, this will use the | |
+[`stream_context_create()`](https://secure.php.net/stream_context_create) and | |
+[`file_get_contents()`](https://secure.php.net/file_get_contents) to make a POST | |
+request to the reCAPTCHA service. This is handled by the | |
+[`RequestMethod\Post`](./src/ReCaptcha/RequestMethod/Post.php) class. | |
+ | |
+## Alternate request methods | |
+ | |
+You may need to use other methods for making requests in your environment. The | |
+[`ReCaptcha`](./src/ReCaptcha/ReCaptcha.php) class allows an optional | |
+[`RequestMethod`](./src/ReCaptcha/RequestMethod.php) instance to configure this. | |
+For example, if you want to use [cURL](https://secure.php.net/curl) instead you | |
+can do this: | |
+ | |
+```php | |
+<?php | |
+$recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\CurlPost()); | |
+``` | |
+ | |
+Alternatively, you can also use a [socket](https://secure.php.net/fsockopen): | |
+ | |
+```php | |
+<?php | |
+$recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\SocketPost()); | |
+``` | |
+ | |
+## Adding new request methods | |
+ | |
+Create a class that implements the | |
+[`RequestMethod`](./src/ReCaptcha/RequestMethod.php) interface. The convention | |
+is to name this class `RequestMethod\`_MethodType_`Post` and create a separate | |
+`RequestMethod\`_MethodType_ class that wraps just the calls to the network | |
+calls themselves. This means that the `RequestMethod\`_MethodType_`Post` can be | |
+unit tested by passing in a mock. Take a look at | |
+[`RequestMethod\CurlPost`](./src/ReCaptcha/RequestMethod/CurlPost.php) and | |
+[`RequestMethod\Curl`](./src/ReCaptcha/RequestMethod/Curl.php) with the matching | |
+[`RequestMethod/CurlPostTest`](./tests/ReCaptcha/RequestMethod/CurlPostTest.php) | |
+to see this pattern in action. | |
+ | |
+### Error conventions | |
+ | |
+The client returns the response as provided by the reCAPTCHA services augmented | |
+with additional error codes based on the client's checks. When adding a new | |
+[`RequestMethod`](./src/ReCaptcha/RequestMethod.php) ensure that it returns the | |
+`ReCaptcha::E_CONNECTION_FAILED` and `ReCaptcha::E_BAD_RESPONSE` where | |
+appropriate. | |
diff --git a/recaptcha-php/CONTRIBUTING.md b/recaptcha-php/CONTRIBUTING.md | |
index 110ec21..a236862 100644 | |
--- a/recaptcha-php/CONTRIBUTING.md | |
+++ b/recaptcha-php/CONTRIBUTING.md | |
@@ -1,24 +1,49 @@ | |
-Want to contribute? Great! First, read this page (including the small print at the end). | |
+# Contributing | |
-### Before you contribute | |
-Before we can use your code, you must sign the | |
-[Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) | |
+Want to contribute? Great! First, read this page (including the small print at | |
+the end). | |
+ | |
+## Contributor License Agreement | |
+ | |
+Before we can use your code, you must sign the [Google Individual Contributor | |
+License | |
+Agreement](https://developers.google.com/open-source/cla/individual?csw=1) | |
(CLA), which you can do online. The CLA is necessary mainly because you own the | |
copyright to your changes, even after your contribution becomes part of our | |
codebase, so we need your permission to use and distribute your code. We also | |
need to be sure of various other things—for instance that you'll tell us if you | |
know that your code infringes on other people's patents. You don't have to sign | |
-the CLA until after you've submitted your code for review and a member has | |
-approved it, but you must do it before we can put your code into our codebase. | |
-Before you start working on a larger contribution, you should get in touch with | |
-us first through the issue tracker with your idea so that we can help out and | |
-possibly guide you. Coordinating up front makes it much easier to avoid | |
-frustration later on. | |
+the CLA until after you've submitted your code for review (a link will be | |
+automatically added to your Pull Request) and a member has approved it, but you | |
+must do it before we can put your code into our codebase. Before you start | |
+working on a larger contribution, you should get in touch with us first through | |
+the issue tracker with your idea so that we can help out and possibly guide you. | |
+Coordinating up front makes it much easier to avoid frustration later on. | |
+ | |
+## Linting and testing | |
+ | |
+We use PHP Coding Standards Fixer to maintain coding standards and PHPUnit to | |
+run our tests. For convenience, there are Composer scripts to run each of these: | |
-### Code reviews | |
-All submissions, including submissions by project members, require review. We | |
-use GitHub pull requests for this purpose. | |
+```sh | |
+composer run-script lint | |
+composer run-script test | |
+``` | |
+ | |
+These are run automatically by [Travis | |
+CI](https://travis-ci.org/google/recaptcha) against your Pull Request, but it's | |
+a good idea to run them locally before submission to avoid getting things | |
+bounced back. That said, tests can be a little daunting so feel free to submit | |
+your PR and ask for help. | |
+ | |
+## Code reviews | |
+ | |
+All submissions, including submissions by project members, require review. | |
+Reviews are conducted on the Pull Requests. The reviews are there to ensure and | |
+improve code quality, so treat them like a discussion and opportunity to learn. | |
+Don't get disheartened if your Pull Request isn't just automatically approved. | |
### The small print | |
-Contributions made by corporations are covered by a different agreement than | |
-the one above, the Software Grant and Corporate Contributor License Agreement. | |
+ | |
+Contributions made by corporations are covered by a different agreement than the | |
+one above, the Software Grant and Corporate Contributor License Agreement. | |
diff --git a/recaptcha-php/README.md b/recaptcha-php/README.md | |
index 7984adf..c007553 100644 | |
--- a/recaptcha-php/README.md | |
+++ b/recaptcha-php/README.md | |
@@ -1,115 +1,139 @@ | |
# reCAPTCHA PHP client library | |
[](https://travis-ci.org/google/recaptcha) | |
+[](https://coveralls.io/github/google/recaptcha) | |
[](https://packagist.org/packages/google/recaptcha) | |
[](https://packagist.org/packages/google/recaptcha) | |
-* Project page: http://www.google.com/recaptcha/ | |
-* Repository: https://github.com/google/recaptcha | |
-* Version: 1.1.3 | |
-* License: BSD, see [LICENSE](LICENSE) | |
- | |
-## Description | |
- | |
reCAPTCHA is a free CAPTCHA service that protect websites from spam and abuse. | |
-This is Google authored code that provides plugins for third-party integration | |
-with reCAPTCHA. | |
+This is a PHP library that wraps up the server-side verification step required | |
+to process responses from the reCAPTCHA service. This client supports both v2 | |
+and v3. | |
+ | |
+- reCAPTCHA: https://www.google.com/recaptcha | |
+- This repo: https://github.com/google/recaptcha | |
+- Version: 1.2.1 | |
+- License: BSD, see [LICENSE](LICENSE) | |
## Installation | |
-### Composer (Recommended) | |
+### Composer (recommended) | |
-[Composer](https://getcomposer.org/) is a widely used dependency manager for PHP | |
-packages. This reCAPTCHA client is available on Packagist as | |
-[`google/recaptcha`](https://packagist.org/packages/google/recaptcha) and can be | |
-installed either by running the `composer require` command or adding the library | |
-to your `composer.json`. To enable Composer for you project, refer to the | |
-project's [Getting Started](https://getcomposer.org/doc/00-intro.md) | |
-documentation. | |
+Use [Composer](https://getcomposer.org) to install this library from Packagist: | |
+[`google/recaptcha`](https://packagist.org/packages/google/recaptcha) | |
-To add this dependency using the command, run the following from within your | |
-project directory: | |
-``` | |
-composer require google/recaptcha "~1.1" | |
+Run the following command from your project directory to add the dependency: | |
+ | |
+```sh | |
+composer require google/recaptcha "^1.2" | |
``` | |
Alternatively, add the dependency directly to your `composer.json` file: | |
+ | |
```json | |
"require": { | |
- "google/recaptcha": "~1.1" | |
+ "google/recaptcha": "^1.2" | |
} | |
``` | |
-### Direct download (no Composer) | |
+### Direct download | |
-If you wish to install the library manually (i.e. without Composer), then you | |
-can use the links on the main project page to either clone the repo or download | |
-the [ZIP file](https://github.com/google/recaptcha/archive/master.zip). For | |
-convenience, an autoloader script is provided in `src/autoload.php` which you | |
-can require into your script instead of Composer's `vendor/autoload.php`. For | |
-example: | |
+Download the [ZIP file](https://github.com/google/recaptcha/archive/master.zip) | |
+and extract into your project. An autoloader script is provided in | |
+`src/autoload.php` which you can require into your script. For example: | |
```php | |
-require('/path/to/recaptcha/src/autoload.php'); | |
+require_once '/path/to/recaptcha/src/autoload.php'; | |
$recaptcha = new \ReCaptcha\ReCaptcha($secret); | |
``` | |
The classes in the project are structured according to the | |
-[PSR-4](http://www.php-fig.org/psr/psr-4/) standard, so you may of course also | |
-use your own autoloader or require the needed files directly in your code. | |
+[PSR-4](http://www.php-fig.org/psr/psr-4/) standard, so you can also use your | |
+own autoloader or require the needed files directly in your code. | |
-### Development install | |
+## Usage | |
-If you would like to contribute to this project or run the unit tests on within | |
-your own environment you will need to install the development dependencies, in | |
-this case that means [PHPUnit](https://phpunit.de/). If you clone the repo and | |
-run `composer install` from within the repo, this will also grab PHPUnit and all | |
-its dependencies for you. If you only need the autoloader installed, then you | |
-can always specify to Composer not to run in development mode, e.g. `composer | |
-install --no-dev`. | |
+First obtain the appropriate keys for the type of reCAPTCHA you wish to | |
+integrate for v2 at https://www.google.com/recaptcha/admin or v3 at | |
+https://g.co/recaptcha/v3. | |
-*Note:* These dependencies are only required for development, there's no | |
-requirement for them to be included in your production code. | |
+Then follow the [integration guide on the developer | |
+site](https://developers.google.com/recaptcha/intro) to add the reCAPTCHA | |
+functionality into your frontend. | |
-## Usage | |
+This library comes in when you need to verify the user's response. On the PHP | |
+side you need the response from the reCAPTCHA service and secret key from your | |
+credentials. Instantiate the `ReCaptcha` class with your secret key, specify any | |
+additional validation rules, and then call `verify()` with the reCAPTCHA | |
+response and user's IP address. For example: | |
-First, register keys for your site at https://www.google.com/recaptcha/admin | |
+```php | |
+<?php | |
+$recaptcha = new \ReCaptcha\ReCaptcha($secret); | |
+$resp = $recaptcha->setExpectedHostname('recaptcha-demo.appspot.com') | |
+ ->verify($gRecaptchaResponse, $remoteIp); | |
+if ($resp->isSuccess()) { | |
+ // Verified! | |
+} else { | |
+ $errors = $resp->getErrorCodes(); | |
+} | |
+``` | |
+ | |
+The following methods are available: | |
+ | |
+- `setExpectedHostname($hostname)`: ensures the hostname matches. You must do | |
+ this if you have disabled "Domain/Package Name Validation" for your | |
+ credentials. | |
+- `setExpectedApkPackageName($apkPackageName)`: if you're verifying a response | |
+ from an Android app. Again, you must do this if you have disabled | |
+ "Domain/Package Name Validation" for your credentials. | |
+- `setExpectedAction($action)`: ensures the action matches for the v3 API. | |
+- `setScoreThreshold($threshold)`: set a score theshold for responses from the | |
+ v3 API | |
+- `setChallengeTimeout($timeoutSeconds)`: set a timeout between the user passing | |
+ the reCAPTCHA and your server processing it. | |
+ | |
+Each of the `set`\*`()` methods return the `ReCaptcha` instance so you can chain | |
+them together. For example: | |
-When your app receives a form submission containing the `g-recaptcha-response` | |
-field, you can verify it using: | |
```php | |
<?php | |
$recaptcha = new \ReCaptcha\ReCaptcha($secret); | |
-$resp = $recaptcha->verify($gRecaptchaResponse, $remoteIp); | |
+$resp = $recaptcha->setExpectedHostname('recaptcha-demo.appspot.com') | |
+ ->setExpectedAction('homepage') | |
+ ->setScoreThreshold(0.5) | |
+ ->verify($gRecaptchaResponse, $remoteIp); | |
+ | |
if ($resp->isSuccess()) { | |
- // verified! | |
- // if Domain Name Validation turned off don't forget to check hostname field | |
- // if($resp->getHostName() === $_SERVER['SERVER_NAME']) { } | |
+ // Verified! | |
} else { | |
$errors = $resp->getErrorCodes(); | |
} | |
``` | |
-You can see an end-to-end working example in | |
-[examples/example-captcha.php](examples/example-captcha.php) | |
+You can find the constants for the libraries error codes in the `ReCaptcha` | |
+class constants, e.g. `ReCaptcha::E_HOSTNAME_MISMATCH` | |
+ | |
+For more details on usage and structure, see [ARCHITECTURE](ARCHITECTURE.md). | |
-## Upgrading | |
+### Examples | |
-### From 1.0.0 | |
+You can see examples of each reCAPTCHA type in [examples/](examples/). You can | |
+run the examples locally by using the Composer script: | |
+ | |
+```sh | |
+composer run-script serve-examples | |
+``` | |
-The previous version of this client is still available on the `1.0.0` tag [in | |
-this repo](https://github.com/google/recaptcha/tree/1.0.0) but it is purely for | |
-reference and will not receive any updates. | |
+This makes use of the in-built PHP dev server to host the examples at | |
+http://localhost:8080/ | |
-The major changes in 1.1.0 are: | |
-* installation now via Composer; | |
-* class loading also via Composer; | |
-* classes now namespaced; | |
-* old method call was `$rc->verifyResponse($remoteIp, $response)`, new call is | |
- `$rc->verify($response, $remoteIp)` | |
+These are also hosted on Google AppEngine Flexible environment at | |
+https://recaptcha-demo.appspot.com/. This is configured by | |
+[`app.yaml`](./app.yaml) which you can also use to [deploy to your own AppEngine | |
+project](https://cloud.google.com/appengine/docs/flexible/php/download). | |
## Contributing | |
-We accept contributions via GitHub Pull Requests, but all contributors need to | |
-be covered by the standard Google Contributor License Agreement. You can find | |
-instructions for this in [CONTRIBUTING](CONTRIBUTING.md) | |
+No one ever has enough engineers, so we're very happy to accept contributions | |
+via Pull Requests. For details, see [CONTRIBUTING](CONTRIBUTING.md) | |
diff --git a/recaptcha-php/app.yaml b/recaptcha-php/app.yaml | |
new file mode 100644 | |
index 0000000..b6ccaf1 | |
--- /dev/null | |
+++ b/recaptcha-php/app.yaml | |
@@ -0,0 +1,8 @@ | |
+runtime: php | |
+env: flex | |
+ | |
+skip_files: | |
+- tests | |
+ | |
+runtime_config: | |
+ document_root: examples | |
diff --git a/recaptcha-php/composer.json b/recaptcha-php/composer.json | |
index 1b41db8..d4695b7 100644 | |
--- a/recaptcha-php/composer.json | |
+++ b/recaptcha-php/composer.json | |
@@ -1,9 +1,9 @@ | |
{ | |
"name": "google/recaptcha", | |
- "description": "Client library for reCAPTCHA, a free service that protect websites from spam and abuse.", | |
+ "description": "Client library for reCAPTCHA, a free service that protects websites from spam and abuse.", | |
"type": "library", | |
"keywords": ["recaptcha", "captcha", "spam", "abuse"], | |
- "homepage": "http://www.google.com/recaptcha/", | |
+ "homepage": "https://www.google.com/recaptcha/", | |
"license": "BSD-3-Clause", | |
"support": { | |
"forum": "https://groups.google.com/forum/#!forum/recaptcha", | |
@@ -13,7 +13,9 @@ | |
"php": ">=5.5" | |
}, | |
"require-dev": { | |
- "phpunit/phpunit": "^4.8" | |
+ "phpunit/phpunit": "^4.8.36|^5.7.27|^6.59|^7", | |
+ "friendsofphp/php-cs-fixer": "^2.2.20|^2.12", | |
+ "php-coveralls/php-coveralls": "^2.1" | |
}, | |
"autoload": { | |
"psr-4": { | |
@@ -22,7 +24,16 @@ | |
}, | |
"extra": { | |
"branch-alias": { | |
- "dev-master": "1.1.x-dev" | |
+ "dev-master": "1.2.x-dev" | |
} | |
+ }, | |
+ "scripts": { | |
+ "lint": "vendor/bin/php-cs-fixer -vvv fix --using-cache=no --dry-run .", | |
+ "lint-fix": "vendor/bin/php-cs-fixer -vvv fix --using-cache=no .", | |
+ "test": "vendor/bin/phpunit --colors=always", | |
+ "serve-examples": "@php -S localhost:8080 -t examples" | |
+ }, | |
+ "config": { | |
+ "process-timeout": 0 | |
} | |
} | |
diff --git a/recaptcha-php/examples/appengine-https.php b/recaptcha-php/examples/appengine-https.php | |
new file mode 100644 | |
index 0000000..fb6feca | |
--- /dev/null | |
+++ b/recaptcha-php/examples/appengine-https.php | |
@@ -0,0 +1,33 @@ | |
+<?php | |
+/** | |
+ * @copyright Copyright (c) 2015, Google Inc. | |
+ * @link https://www.google.com/recaptcha | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a copy | |
+ * of this software and associated documentation files (the "Software"), to deal | |
+ * in the Software without restriction, including without limitation the rights | |
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
+ * copies of the Software, and to permit persons to whom the Software is | |
+ * furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice shall be included in | |
+ * all copies or substantial portions of the Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
+ * THE SOFTWARE. | |
+ */ | |
+// Redirect to HTTPS by default (for AppEngine) | |
+if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { | |
+ if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'http') { | |
+ header('HTTP/1.1 301 Moved Permanently'); | |
+ header('Location: https://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI']); | |
+ exit(0); | |
+ } else { | |
+ header('Strict-Transport-Security: max-age=63072000; includeSubDomains; preload'); | |
+ } | |
+} | |
diff --git a/recaptcha-php/examples/config.php.dist b/recaptcha-php/examples/config.php.dist | |
new file mode 100644 | |
index 0000000..faea194 | |
--- /dev/null | |
+++ b/recaptcha-php/examples/config.php.dist | |
@@ -0,0 +1,15 @@ | |
+<?php | |
+return [ | |
+ 'v2-standard' => [ | |
+ 'site' => '', | |
+ 'secret' => '', | |
+ ], | |
+ 'v2-invisible' => [ | |
+ 'site' => '', | |
+ 'secret' => '', | |
+ ], | |
+ 'v3' => [ | |
+ 'site' => '', | |
+ 'secret' => '', | |
+ ], | |
+]; | |
diff --git a/recaptcha-php/examples/example-captcha.php b/recaptcha-php/examples/example-captcha.php | |
deleted file mode 100644 | |
index f636cc1..0000000 | |
--- a/recaptcha-php/examples/example-captcha.php | |
+++ /dev/null | |
@@ -1,130 +0,0 @@ | |
-<?php | |
-/** | |
- * Working sample code to accompany the library. The instructions here assume | |
- * you've just cloned the repo. If you've installed via composer, you will want | |
- * to adjust the path to the autoloader. | |
- * | |
- * 1. Run the server. For example, under Linux you can probably use: | |
- * /usr/bin/php -S "localhost:8000" "examples/example-captcha.php" | |
- * 2. Point your browser at http://localhost:8000 | |
- * 3. Follow the instructions | |
- * | |
- * @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
- * | |
- * Permission is hereby granted, free of charge, to any person obtaining a copy | |
- * of this software and associated documentation files (the "Software"), to deal | |
- * in the Software without restriction, including without limitation the rights | |
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
- * copies of the Software, and to permit persons to whom the Software is | |
- * furnished to do so, subject to the following conditions: | |
- * | |
- * The above copyright notice and this permission notice shall be included in | |
- * all copies or substantial portions of the Software. | |
- * | |
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
- * THE SOFTWARE. | |
- */ | |
-// Initiate the autoloader. The file should be generated by Composer. | |
-// You will provide your own autoloader or require the files directly if you did | |
-// not install via Composer. | |
-require_once __DIR__ . '/../vendor/autoload.php'; | |
- | |
-// Register API keys at https://www.google.com/recaptcha/admin | |
-$siteKey = ''; | |
-$secret = ''; | |
- | |
-// reCAPTCHA supported 40+ languages listed here: https://developers.google.com/recaptcha/docs/language | |
-$lang = 'en'; | |
-?> | |
-<html> | |
- <head> | |
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> | |
- <title>reCAPTCHA Example</title> | |
- <link rel="shortcut icon" href="//www.gstatic.com/recaptcha/admin/favicon.ico" type="image/x-icon"/> | |
- <style type="text/css"> | |
- body { | |
- margin: 1em 5em 0 5em; | |
- font-family: sans-serif; | |
- } | |
- fieldset { | |
- display: inline; | |
- padding: 1em; | |
- } | |
- </style> | |
- </head> | |
- <body> | |
- <h1>reCAPTCHA Example</h1> | |
- <?php if ($siteKey === '' || $secret === ''): ?> | |
- <h2>Add your keys</h2> | |
- <p>If you do not have keys already then visit <kbd> | |
- <a href = "https://www.google.com/recaptcha/admin"> | |
- https://www.google.com/recaptcha/admin</a></kbd> to generate them. | |
- Edit this file and set the respective keys in <kbd>$siteKey</kbd> and | |
- <kbd>$secret</kbd>. Reload the page after this.</p> | |
- <?php | |
-elseif (isset($_POST['g-recaptcha-response'])): | |
- // The POST data here is unfiltered because this is an example. | |
- // In production, *always* sanitise and validate your input' | |
- ?> | |
- <h2><kbd>POST</kbd> data</h2> | |
- <kbd><pre><?php var_export($_POST); ?></pre></kbd> | |
- <?php | |
-// If the form submission includes the "g-captcha-response" field | |
-// Create an instance of the service using your secret | |
- $recaptcha = new \ReCaptcha\ReCaptcha($secret); | |
- | |
-// If file_get_contents() is locked down on your PHP installation to disallow | |
-// its use with URLs, then you can use the alternative request method instead. | |
-// This makes use of fsockopen() instead. | |
-// $recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\SocketPost()); | |
- | |
-// Make the call to verify the response and also pass the user's IP address | |
- $resp = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); | |
- | |
- if ($resp->isSuccess()): | |
-// If the response is a success, that's it! | |
- ?> | |
- <h2>Success!</h2> | |
- <p>That's it. Everything is working. Go integrate this into your real project.</p> | |
- <p><a href="/">Try again</a></p> | |
- <?php | |
- else: | |
-// If it's not successful, then one or more error codes will be returned. | |
- ?> | |
- <h2>Something went wrong</h2> | |
- <p>The following error was returned: <?php | |
- foreach ($resp->getErrorCodes() as $code) { | |
- echo '<kbd>' , $code , '</kbd> '; | |
- } | |
- ?></p> | |
- <p>Check the error code reference at <kbd><a href="https://developers.google.com/recaptcha/docs/verify#error-code-reference">https://developers.google.com/recaptcha/docs/verify#error-code-reference</a></kbd>. | |
- <p><strong>Note:</strong> Error code <kbd>missing-input-response</kbd> may mean the user just didn't complete the reCAPTCHA.</p> | |
- <p><a href="/">Try again</a></p> | |
- <?php | |
- endif; | |
-else: | |
-// Add the g-recaptcha tag to the form you want to include the reCAPTCHA element | |
- ?> | |
- <p>Complete the reCAPTCHA then submit the form.</p> | |
- <form action="/" method="post"> | |
- <fieldset> | |
- <legend>An example form</legend> | |
- <p>Example input A: <input type="text" name="ex-a" value="foo"></p> | |
- <p>Example input B: <input type="text" name="ex-b" value="bar"></p> | |
- | |
- <div class="g-recaptcha" data-sitekey="<?php echo $siteKey; ?>"></div> | |
- <script type="text/javascript" | |
- src="https://www.google.com/recaptcha/api.js?hl=<?php echo $lang; ?>"> | |
- </script> | |
- <p><input type="submit" value="Submit" /></p> | |
- </fieldset> | |
- </form> | |
-<?php endif; ?> | |
-</body> | |
-</html> | |
diff --git a/recaptcha-php/examples/examples.css b/recaptcha-php/examples/examples.css | |
new file mode 100644 | |
index 0000000..3d10aa7 | |
--- /dev/null | |
+++ b/recaptcha-php/examples/examples.css | |
@@ -0,0 +1,37 @@ | |
+body { | |
+ font-family: sans-serif; | |
+ margin: 0; | |
+ padding: 0; | |
+} | |
+ | |
+h1, | |
+h2, | |
+p { | |
+ margin: 0; | |
+ padding: 0.5rem 0 0 0; | |
+ font-weight: normal; | |
+} | |
+ | |
+h1, | |
+h2 { | |
+ color: #222244; | |
+} | |
+ | |
+h2 { | |
+ font-style: italic; | |
+} | |
+ | |
+header { | |
+ padding: 0.5rem 2rem 0.5rem 2rem; | |
+ background: #f0f0f4; | |
+ border-bottom: 1px solid #aaaabb; | |
+} | |
+ | |
+main { | |
+ padding: 0.5rem 2rem 0.5rem 2rem; | |
+} | |
+ | |
+.form-field { | |
+ display: block; | |
+ margin: 1rem; | |
+} | |
diff --git a/recaptcha-php/examples/google0afd8760fd68f119.html b/recaptcha-php/examples/google0afd8760fd68f119.html | |
new file mode 100644 | |
index 0000000..457c471 | |
--- /dev/null | |
+++ b/recaptcha-php/examples/google0afd8760fd68f119.html | |
@@ -0,0 +1 @@ | |
+google-site-verification: google0afd8760fd68f119.html | |
\ No newline at end of file | |
diff --git a/recaptcha-php/examples/index.php b/recaptcha-php/examples/index.php | |
new file mode 100644 | |
index 0000000..fec3158 | |
--- /dev/null | |
+++ b/recaptcha-php/examples/index.php | |
@@ -0,0 +1,65 @@ | |
+<?php | |
+/** | |
+ * @copyright Copyright (c) 2015, Google Inc. | |
+ * @link https://www.google.com/recaptcha | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a copy | |
+ * of this software and associated documentation files (the "Software"), to deal | |
+ * in the Software without restriction, including without limitation the rights | |
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
+ * copies of the Software, and to permit persons to whom the Software is | |
+ * furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice shall be included in | |
+ * all copies or substantial portions of the Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
+ * THE SOFTWARE. | |
+ */ | |
+require __DIR__ . '/appengine-https.php'; | |
+?> | |
+<!DOCTYPE html> | |
+<html lang="en"> | |
+<meta charset="UTF-8"> | |
+<meta name="viewport" content="width=device-width,height=device-height,minimum-scale=1"> | |
+<link rel="shortcut icon" href="https://www.gstatic.com/recaptcha/admin/favicon.ico" type="image/x-icon"/> | |
+<link rel="canonical" href="https://recaptcha-demo.appspot.com/"> | |
+<script type="application/ld+json">{ "@context": "http://schema.org", "@type": "WebSite", "name": "reCAPTCHA demo", "url": "http://recaptcha-demo.appspot.com/" }</script> | |
+<meta name="description" content="reCAPTCHA demo" /> | |
+<meta property="og:url" content="https://recaptcha-demo.appspot.com/" /> | |
+<meta property="og:type" content="website" /> | |
+<meta property="og:title" content="reCAPTCHA demo" /> | |
+<meta property="og:description" content="Examples of the reCAPTCHA client." /> | |
+<link rel="stylesheet" type="text/css" href="/examples.css"> | |
+<title>reCAPTCHA demo</title> | |
+ | |
+<header> | |
+ <h1>reCAPTCHA demo</h1> | |
+</header> | |
+<main> | |
+ <p>Try out the various forms of <a href="https://www.google.com/recaptcha/">reCAPTCHA</a>.</p> | |
+ <p>You can find the source code for these examples on GitHub in <kbd><a href="https://github.com/google/recaptcha">google/recaptcha</a></kbd>.</p> | |
+ <ul> | |
+ <li><h2>reCAPTCHA v2</h2> | |
+ <ul> | |
+ <li><a href="/recaptcha-v2-checkbox.php">"I'm not a robot" checkbox</a></li> | |
+ <li><a href="/recaptcha-v2-checkbox-explicit.php">"I'm not a robot" checkbox - Explicit render</a></li> | |
+ <li><a href="/recaptcha-v2-invisible.php">Invisible</a></li> | |
+ </ul> | |
+ </li> | |
+ <li><h2>reCAPTCHA v3</h2> | |
+ <ul> | |
+ <li><a href="/recaptcha-v3-request-scores.php">Request scores</a></li> | |
+ </ul> | |
+ </li> | |
+ </ul> | |
+</main> | |
+ | |
+<!-- Google Analytics - just ignore this --> | |
+<script async src="https://www.googletagmanager.com/gtag/js?id=UA-123057962-1"></script> | |
+<script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-123057962-1');</script> | |
diff --git a/recaptcha-php/examples/recaptcha-v2-checkbox-explicit.php b/recaptcha-php/examples/recaptcha-v2-checkbox-explicit.php | |
new file mode 100644 | |
index 0000000..5a53f19 | |
--- /dev/null | |
+++ b/recaptcha-php/examples/recaptcha-v2-checkbox-explicit.php | |
@@ -0,0 +1,139 @@ | |
+<?php | |
+/** | |
+ * @copyright Copyright (c) 2015, Google Inc. | |
+ * @link https://www.google.com/recaptcha | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a copy | |
+ * of this software and associated documentation files (the "Software"), to deal | |
+ * in the Software without restriction, including without limitation the rights | |
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
+ * copies of the Software, and to permit persons to whom the Software is | |
+ * furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice shall be included in | |
+ * all copies or substantial portions of the Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
+ * THE SOFTWARE. | |
+ */ | |
+require __DIR__ . '/appengine-https.php'; | |
+ | |
+// Initiate the autoloader. The file should be generated by Composer. | |
+// You will provide your own autoloader or require the files directly if you did | |
+// not install via Composer. | |
+require_once __DIR__ . '/../vendor/autoload.php'; | |
+ | |
+// Register API keys at https://www.google.com/recaptcha/admin | |
+$siteKey = ''; | |
+$secret = ''; | |
+ | |
+// Copy the config.php.dist file to config.php and update it with your keys to run the examples | |
+if ($siteKey == '' && is_readable(__DIR__ . '/config.php')) { | |
+ $config = include __DIR__ . '/config.php'; | |
+ $siteKey = $config['v2-standard']['site']; | |
+ $secret = $config['v2-standard']['secret']; | |
+} | |
+ | |
+// reCAPTCHA supports 40+ languages listed here: https://developers.google.com/recaptcha/docs/language | |
+$lang = 'en'; | |
+?> | |
+<!DOCTYPE html> | |
+<html lang="en"> | |
+<meta charset="UTF-8"> | |
+<meta name="viewport" content="width=device-width,height=device-height,minimum-scale=1"> | |
+<link rel="shortcut icon" href="https://www.gstatic.com/recaptcha/admin/favicon.ico" type="image/x-icon"/> | |
+<link rel="canonical" href="https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox-explicit.php"> | |
+<script type="application/ld+json">{ "@context": "http://schema.org", "@type": "WebSite", "name": "reCAPTCHA demo - \"I'm not a robot\" checkbox - Explicit render", "url": "https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox-explicit.php" }</script> | |
+<meta name="description" content="reCAPTCHA demo - "I'm not a robot" checkbox - Explicit render" /> | |
+<meta property="og:url" content="https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox-explicit.php" /> | |
+<meta property="og:type" content="website" /> | |
+<meta property="og:title" content="reCAPTCHA demo - "I'm not a robot" checkbox - Explicit render" /> | |
+<meta property="og:description" content="reCAPTCHA demo - "I'm not a robot" checkbox - Explicit render" /> | |
+<link rel="stylesheet" type="text/css" href="/examples.css"> | |
+<title>reCAPTCHA demo - "I'm not a robot" checkbox - Explicit render</title> | |
+ | |
+<header> | |
+ <h1>reCAPTCHA demo</h1><h2>"I'm not a robot" checkbox - Explicit render</h2> | |
+ <p><a href="/">↤ Home</a></p> | |
+</header> | |
+<main> | |
+<?php | |
+if ($siteKey === '' || $secret === ''): | |
+?> | |
+ <h2>Add your keys</h2> | |
+ <p>If you do not have keys already then visit <kbd> <a href = "https://www.google.com/recaptcha/admin">https://www.google.com/recaptcha/admin</a></kbd> to generate them. Edit this file and set the respective keys in the <kbd>config.php</kbd> file or directly to <kbd>$siteKey</kbd> and <kbd>$secret</kbd>. Reload the page after this.</p> | |
+ <?php | |
+elseif (isset($_POST['g-recaptcha-response'])): | |
+ // The POST data here is unfiltered because this is an example. | |
+ // In production, *always* sanitise and validate your input' | |
+ ?> | |
+ <h2><kbd>POST</kbd> data</h2> | |
+ <kbd><pre><?php var_export($_POST);?></pre></kbd> | |
+ <?php | |
+ // If the form submission includes the "g-captcha-response" field | |
+ // Create an instance of the service using your secret | |
+ $recaptcha = new \ReCaptcha\ReCaptcha($secret); | |
+ | |
+ // If file_get_contents() is locked down on your PHP installation to disallow | |
+ // its use with URLs, then you can use the alternative request method instead. | |
+ // This makes use of fsockopen() instead. | |
+ // $recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\SocketPost()); | |
+ // Make the call to verify the response and also pass the user's IP address | |
+ $resp = $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME']) | |
+ ->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); | |
+ | |
+ if ($resp->isSuccess()): | |
+ // If the response is a success, that's it! | |
+ ?> | |
+ <h2>Success!</h2> | |
+ <kbd><pre><?php var_export($resp);?></pre></kbd> | |
+ <p>That's it. Everything is working. Go integrate this into your real project.</p> | |
+ <p><a href="/recaptcha-v2-checkbox-explicit.php">⟳ Try again</a></p> | |
+ <?php | |
+ else: | |
+ // If it's not successful, then one or more error codes will be returned. | |
+ ?> | |
+ <h2>Something went wrong</h2> | |
+ <kbd><pre><?php var_export($resp);?></pre></kbd> | |
+ <p>Check the error code reference at <kbd><a href="https://developers.google.com/recaptcha/docs/verify#error-code-reference">https://developers.google.com/recaptcha/docs/verify#error-code-reference</a></kbd>. | |
+ <p><strong>Note:</strong> Error code <kbd>missing-input-response</kbd> may mean the user just didn't complete the reCAPTCHA.</p> | |
+ <p><a href="/recaptcha-v2-checkbox-explicit.php">⟳ Try again</a></p> | |
+ <?php | |
+ endif; | |
+else: | |
+ // Add the g-recaptcha tag to the form you want to include the reCAPTCHA element | |
+ ?> | |
+ <p>Complete the reCAPTCHA then submit the form.</p> | |
+ <form action="/recaptcha-v2-checkbox-explicit.php" method="post"> | |
+ <fieldset> | |
+ <legend>An example form</legend> | |
+ <label class="form-field">Example input A: <input type="text" name="ex-a" value="foo"></label> | |
+ <label class="form-field">Example input B: <input type="text" name="ex-b" value="bar"></label> | |
+ <!-- Set up a container to render the widget --> | |
+ <div class="g-recaptcha form-field"></div> | |
+ <!-- Disable the button by default, will enable when the widget loads --> | |
+ <button class="form-field" type="submit" disabled>Submit ↦</button> | |
+ </fieldset> | |
+ </form> | |
+ <script type="text/javascript"> | |
+ var onloadCallback = function() { | |
+ var captchaContainer = document.querySelector('.g-recaptcha'); | |
+ grecaptcha.render(captchaContainer, { | |
+ 'sitekey' : '<?php echo $siteKey; ?>' | |
+ }); | |
+ document.querySelector('button[type="submit"]').disabled = false; | |
+ }; | |
+ </script> | |
+ <script type="text/javascript" src="https://www.google.com/recaptcha/api.js?hl=<?php echo $lang; ?>&onload=onloadCallback&render=explicit" async defer></script> | |
+ <?php | |
+endif;?> | |
+</main> | |
+ | |
+<!-- Google Analytics - just ignore this --> | |
+<script async src="https://www.googletagmanager.com/gtag/js?id=UA-123057962-1"></script> | |
+<script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-123057962-1');</script> | |
diff --git a/recaptcha-php/examples/recaptcha-v2-checkbox.php b/recaptcha-php/examples/recaptcha-v2-checkbox.php | |
new file mode 100644 | |
index 0000000..6f66c58 | |
--- /dev/null | |
+++ b/recaptcha-php/examples/recaptcha-v2-checkbox.php | |
@@ -0,0 +1,130 @@ | |
+<?php | |
+/** | |
+ * @copyright Copyright (c) 2015, Google Inc. | |
+ * @link https://www.google.com/recaptcha | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a copy | |
+ * of this software and associated documentation files (the "Software"), to deal | |
+ * in the Software without restriction, including without limitation the rights | |
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
+ * copies of the Software, and to permit persons to whom the Software is | |
+ * furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice shall be included in | |
+ * all copies or substantial portions of the Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
+ * THE SOFTWARE. | |
+ */ | |
+require __DIR__ . '/appengine-https.php'; | |
+ | |
+ // Initiate the autoloader. The file should be generated by Composer. | |
+// You will provide your own autoloader or require the files directly if you did | |
+// not install via Composer. | |
+require_once __DIR__ . '/../vendor/autoload.php'; | |
+ | |
+// Register API keys at https://www.google.com/recaptcha/admin | |
+$siteKey = ''; | |
+$secret = ''; | |
+ | |
+// Copy the config.php.dist file to config.php and update it with your keys to run the examples | |
+if ($siteKey == '' && is_readable(__DIR__ . '/config.php')) { | |
+ $config = include __DIR__ . '/config.php'; | |
+ $siteKey = $config['v2-standard']['site']; | |
+ $secret = $config['v2-standard']['secret']; | |
+} | |
+ | |
+// reCAPTCHA supports 40+ languages listed here: https://developers.google.com/recaptcha/docs/language | |
+$lang = 'en'; | |
+?> | |
+<!DOCTYPE html> | |
+<html lang="en"> | |
+<meta charset="UTF-8"> | |
+<meta name="viewport" content="width=device-width,height=device-height,minimum-scale=1"> | |
+<link rel="shortcut icon" href="https://www.gstatic.com/recaptcha/admin/favicon.ico" type="image/x-icon"/> | |
+<link rel="canonical" href="https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php"> | |
+<script type="application/ld+json">{ "@context": "http://schema.org", "@type": "WebSite", "name": "reCAPTCHA demo - \"I'm not a robot\" checkbox", "url": "https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php" }</script> | |
+<meta name="description" content="reCAPTCHA demo - "I'm not a robot" checkbox" /> | |
+<meta property="og:url" content="https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php" /> | |
+<meta property="og:type" content="website" /> | |
+<meta property="og:title" content="reCAPTCHA demo - "I'm not a robot" checkbox" /> | |
+<meta property="og:description" content="reCAPTCHA demo - "I'm not a robot" checkbox" /> | |
+<link rel="stylesheet" type="text/css" href="/examples.css"> | |
+<title>reCAPTCHA demo - "I'm not a robot" checkbox</title> | |
+ | |
+<header> | |
+ <h1>reCAPTCHA demo</h1><h2>"I'm not a robot" checkbox</h2> | |
+ <p><a href="/">↤ Home</a></p> | |
+</header> | |
+<main> | |
+<?php | |
+if ($siteKey === '' || $secret === ''): | |
+?> | |
+ <h2>Add your keys</h2> | |
+ <p>If you do not have keys already then visit <kbd> <a href = "https://www.google.com/recaptcha/admin">https://www.google.com/recaptcha/admin</a></kbd> to generate them. Edit this file and set the respective keys in the <kbd>config.php</kbd> file or directly to <kbd>$siteKey</kbd> and <kbd>$secret</kbd>. Reload the page after this.</p> | |
+ <?php | |
+elseif (isset($_POST['g-recaptcha-response'])): | |
+ // The POST data here is unfiltered because this is an example. | |
+ // In production, *always* sanitise and validate your input' | |
+ ?> | |
+ <h2><kbd>POST</kbd> data</h2> | |
+ <kbd><pre><?php var_export($_POST);?></pre></kbd> | |
+ <?php | |
+ // If the form submission includes the "g-captcha-response" field | |
+ // Create an instance of the service using your secret | |
+ $recaptcha = new \ReCaptcha\ReCaptcha($secret); | |
+ | |
+ // If file_get_contents() is locked down on your PHP installation to disallow | |
+ // its use with URLs, then you can use the alternative request method instead. | |
+ // This makes use of fsockopen() instead. | |
+ // $recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\SocketPost()); | |
+ | |
+ // Make the call to verify the response and also pass the user's IP address | |
+ $resp = $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME']) | |
+ ->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); | |
+ if ($resp->isSuccess()): | |
+ // If the response is a success, that's it! | |
+ ?> | |
+ <h2>Success!</h2> | |
+ <kbd><pre><?php var_export($resp);?></pre></kbd> | |
+ <p>That's it. Everything is working. Go integrate this into your real project.</p> | |
+ <p><a href="/recaptcha-v2-checkbox.php">⟳ Try again</a></p> | |
+ <?php | |
+ else: | |
+ // If it's not successful, then one or more error codes will be returned. | |
+ ?> | |
+ <h2>Something went wrong</h2> | |
+ <kbd><pre><?php var_export($resp);?></pre></kbd> | |
+ <p>Check the error code reference at <kbd><a href="https://developers.google.com/recaptcha/docs/verify#error-code-reference">https://developers.google.com/recaptcha/docs/verify#error-code-reference</a></kbd>. | |
+ <p><strong>Note:</strong> Error code <kbd>missing-input-response</kbd> may mean the user just didn't complete the reCAPTCHA.</p> | |
+ <p><a href="/recaptcha-v2-checkbox.php">⟳ Try again</a></p> | |
+ <?php | |
+ endif; | |
+else: | |
+ // Add the g-recaptcha tag to the form you want to include the reCAPTCHA element | |
+ ?> | |
+ <p>Complete the reCAPTCHA then submit the form.</p> | |
+ <form action="/recaptcha-v2-checkbox.php" method="post"> | |
+ <fieldset> | |
+ <legend>An example form</legend> | |
+ <label class="form-field">Example input A: <input type="text" name="ex-a" value="foo"></label> | |
+ <label class="form-field">Example input B: <input type="text" name="ex-b" value="bar"></label> | |
+ <!-- Default behaviour looks for the g-recaptcha class with a data-sitekey attribute --> | |
+ <div class="g-recaptcha form-field" data-sitekey="<?php echo $siteKey; ?>"></div> | |
+ <!-- Submitting before the widget loads will result in a missing-input-response error so you need to verify server side --> | |
+ <button class="form-field" type="submit">Submit ↦</button> | |
+ </fieldset> | |
+ </form> | |
+ <script type="text/javascript" src="https://www.google.com/recaptcha/api.js?hl=<?php echo $lang; ?>"></script> | |
+ <?php | |
+endif;?> | |
+</main> | |
+ | |
+<!-- Google Analytics - just ignore this --> | |
+<script async src="https://www.googletagmanager.com/gtag/js?id=UA-123057962-1"></script> | |
+<script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-123057962-1');</script> | |
diff --git a/recaptcha-php/examples/recaptcha-v2-invisible.php b/recaptcha-php/examples/recaptcha-v2-invisible.php | |
new file mode 100644 | |
index 0000000..e1aba64 | |
--- /dev/null | |
+++ b/recaptcha-php/examples/recaptcha-v2-invisible.php | |
@@ -0,0 +1,132 @@ | |
+<?php | |
+/** | |
+ * @copyright Copyright (c) 2015, Google Inc. | |
+ * @link https://www.google.com/recaptcha | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a copy | |
+ * of this software and associated documentation files (the "Software"), to deal | |
+ * in the Software without restriction, including without limitation the rights | |
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
+ * copies of the Software, and to permit persons to whom the Software is | |
+ * furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice shall be included in | |
+ * all copies or substantial portions of the Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
+ * THE SOFTWARE. | |
+ */ | |
+require __DIR__ . '/appengine-https.php'; | |
+ | |
+ // Initiate the autoloader. The file should be generated by Composer. | |
+// You will provide your own autoloader or require the files directly if you did | |
+// not install via Composer. | |
+require_once __DIR__ . '/../vendor/autoload.php'; | |
+ | |
+// Register API keys at https://www.google.com/recaptcha/admin | |
+$siteKey = ''; | |
+$secret = ''; | |
+ | |
+// Copy the config.php.dist file to config.php and update it with your keys to run the examples | |
+if ($siteKey == '' && is_readable(__DIR__ . '/config.php')) { | |
+ $config = include __DIR__ . '/config.php'; | |
+ $siteKey = $config['v2-invisible']['site']; | |
+ $secret = $config['v2-invisible']['secret']; | |
+} | |
+ | |
+// reCAPTCHA supports 40+ languages listed here: https://developers.google.com/recaptcha/docs/language | |
+$lang = 'en'; | |
+?> | |
+<!DOCTYPE html> | |
+<html lang="en"> | |
+<meta charset="UTF-8"> | |
+<meta name="viewport" content="width=device-width,height=device-height,minimum-scale=1"> | |
+<link rel="shortcut icon" href="https://www.gstatic.com/recaptcha/admin/favicon.ico" type="image/x-icon"/> | |
+<link rel="canonical" href="https://recaptcha-demo.appspot.com/recaptcha-v2-invisible.php"> | |
+<script type="application/ld+json">{ "@context": "http://schema.org", "@type": "WebSite", "name": "reCAPTCHA demo - Invisible", "url": "https://recaptcha-demo.appspot.com/recaptcha-v2-invisible.php" }</script> | |
+<meta name="description" content="reCAPTCHA demo - Invisible" /> | |
+<meta property="og:url" content="https://recaptcha-demo.appspot.com/recaptcha-v2-invisible.php" /> | |
+<meta property="og:type" content="website" /> | |
+<meta property="og:title" content="reCAPTCHA demo - Invisible" /> | |
+<meta property="og:description" content="reCAPTCHA demo - Invisible" /> | |
+<link rel="stylesheet" type="text/css" href="/examples.css"> | |
+<title>reCAPTCHA demo - Invisible</title> | |
+ | |
+<header> | |
+ <h1>reCAPTCHA demo</h1><h2>Invisible</h2> | |
+ <p><a href="/">↤ Home</a></p> | |
+</header> | |
+<main> | |
+<?php | |
+if ($siteKey === '' || $secret === ''): | |
+?> | |
+ <h2>Add your keys</h2> | |
+ <p>If you do not have keys already then visit <kbd> <a href = "https://www.google.com/recaptcha/admin">https://www.google.com/recaptcha/admin</a></kbd> to generate them. Edit this file and set the respective keys in <kbd>$siteKey</kbd> and <kbd>$secret</kbd>. Reload the page after this.</p> | |
+ <?php | |
+elseif (isset($_POST['g-recaptcha-response'])): | |
+ // The POST data here is unfiltered because this is an example. | |
+ // In production, *always* sanitise and validate your input' | |
+ ?> | |
+ <h2><kbd>POST</kbd> data</h2> | |
+ <kbd><pre><?php var_export($_POST);?></pre></kbd> | |
+ <?php | |
+ // If the form submission includes the "g-captcha-response" field | |
+ // Create an instance of the service using your secret | |
+ $recaptcha = new \ReCaptcha\ReCaptcha($secret); | |
+ | |
+ // If file_get_contents() is locked down on your PHP installation to disallow | |
+ // its use with URLs, then you can use the alternative request method instead. | |
+ // This makes use of fsockopen() instead. | |
+ // $recaptcha = new \ReCaptcha\ReCaptcha($secret, new \ReCaptcha\RequestMethod\SocketPost()); | |
+ | |
+ // Make the call to verify the response and also pass the user's IP address | |
+ $resp = $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME']) | |
+ ->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']); | |
+ if ($resp->isSuccess()): | |
+ // If the response is a success, that's it! | |
+ ?> | |
+ <h2>Success!</h2> | |
+ <kbd><pre><?php var_export($resp);?></pre></kbd> | |
+ <p>That's it. Everything is working. Go integrate this into your real project.</p> | |
+ <p><a href="/recaptcha-v2-invisible.php">⟳ Try again</a></p> | |
+ <?php | |
+ else: | |
+ // If it's not successful, then one or more error codes will be returned. | |
+ ?> | |
+ <h2>Something went wrong</h2> | |
+ <kbd><pre><?php var_export($resp);?></pre></kbd> | |
+ <p>Check the error code reference at <kbd><a href="https://developers.google.com/recaptcha/docs/verify#error-code-reference">https://developers.google.com/recaptcha/docs/verify#error-code-reference</a></kbd>. | |
+ <p><strong>Note:</strong> Error code <kbd>missing-input-response</kbd> may mean the user just didn't complete the reCAPTCHA.</p> | |
+ <p><a href="/recaptcha-v2-invisible.php">⟳ Try again</a></p> | |
+ <?php | |
+ endif; | |
+else: | |
+ // Add the g-recaptcha tag to the form you want to include the reCAPTCHA element | |
+ ?> | |
+ <p>Submit the form and reCAPTCHA will run automatically.</p> | |
+ <form action="/recaptcha-v2-invisible.php" method="post" id="demo-form"> | |
+ <fieldset> | |
+ <legend>An example form</legend> | |
+ <label class="form-field">Example input A: <input type="text" name="ex-a" value="foo"></label> | |
+ <label class="form-field">Example input B: <input type="text" name="ex-b" value="bar"></label> | |
+ <button class="g-recaptcha form-field" data-sitekey="<?php echo $siteKey; ?>" data-callback='onSubmit'>Submit ↦</button> | |
+ </fieldset> | |
+ </form> | |
+ <script type="text/javascript" src="https://www.google.com/recaptcha/api.js?hl=<?php echo $lang; ?>" async defer></script> | |
+ <script type="text/javascript"> | |
+ function onSubmit(token) { | |
+ document.getElementById("demo-form").submit(); | |
+ } | |
+ </script> | |
+ <?php | |
+endif;?> | |
+</main> | |
+ | |
+<!-- Google Analytics - just ignore this --> | |
+<script async src="https://www.googletagmanager.com/gtag/js?id=UA-123057962-1"></script> | |
+<script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-123057962-1');</script> | |
diff --git a/recaptcha-php/examples/recaptcha-v3-request-scores.php b/recaptcha-php/examples/recaptcha-v3-request-scores.php | |
new file mode 100644 | |
index 0000000..6d5efee | |
--- /dev/null | |
+++ b/recaptcha-php/examples/recaptcha-v3-request-scores.php | |
@@ -0,0 +1,109 @@ | |
+<?php | |
+/** | |
+ * @copyright Copyright (c) 2015, Google Inc. | |
+ * @link https://www.google.com/recaptcha | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a copy | |
+ * of this software and associated documentation files (the "Software"), to deal | |
+ * in the Software without restriction, including without limitation the rights | |
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
+ * copies of the Software, and to permit persons to whom the Software is | |
+ * furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice shall be included in | |
+ * all copies or substantial portions of the Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
+ * THE SOFTWARE. | |
+ */ | |
+require __DIR__ . '/appengine-https.php'; | |
+ | |
+// Initiate the autoloader. The file should be generated by Composer. | |
+// You will provide your own autoloader or require the files directly if you did | |
+// not install via Composer. | |
+require_once __DIR__ . '/../vendor/autoload.php'; | |
+ | |
+// Register API keys at https://www.google.com/recaptcha/admin | |
+$siteKey = ''; | |
+$secret = ''; | |
+ | |
+// Copy the config.php.dist file to config.php and update it with your keys to run the examples | |
+if ($siteKey == '' && is_readable(__DIR__ . '/config.php')) { | |
+ $config = include __DIR__ . '/config.php'; | |
+ $siteKey = $config['v3']['site']; | |
+ $secret = $config['v3']['secret']; | |
+} | |
+ | |
+// reCAPTCHA supports 40+ languages listed here: https://developers.google.com/recaptcha/docs/language | |
+$lang = 'en'; | |
+ | |
+ | |
+?> | |
+<!DOCTYPE html> | |
+<html lang="en"> | |
+<meta charset="UTF-8"> | |
+<meta name="viewport" content="width=device-width,height=device-height,minimum-scale=1"> | |
+<link rel="shortcut icon" href="https://www.gstatic.com/recaptcha/admin/favicon.ico" type="image/x-icon"/> | |
+<link rel="canonical" href="https://recaptcha-demo.appspot.com/recaptcha-v2-request-scores.php"> | |
+<script type="application/ld+json">{ "@context": "http://schema.org", "@type": "WebSite", "name": "reCAPTCHA demo - Request scores", "url": "https://recaptcha-demo.appspot.com/recaptcha-v2-request-scores.php" }</script> | |
+<meta name="description" content="reCAPTCHA demo - Request scores" /> | |
+<meta property="og:url" content="https://recaptcha-demo.appspot.com/recaptcha-v2-request-scores.php" /> | |
+<meta property="og:type" content="website" /> | |
+<meta property="og:title" content="reCAPTCHA demo - Request scores" /> | |
+<meta property="og:description" content="reCAPTCHA demo - Request scores" /> | |
+<link rel="stylesheet" type="text/css" href="/examples.css"> | |
+<title>reCAPTCHA demo - Request scores</title> | |
+ | |
+<header> | |
+ <h1>reCAPTCHA demo</h1><h2>Request scores</h2> | |
+ <p><a href="/">↤ Home</a></p> | |
+</header> | |
+<main> | |
+<?php | |
+if ($siteKey === '' || $secret === ''): | |
+?> | |
+ <h2>Add your keys</h2> | |
+ <p>If you do not have keys already then visit <kbd> <a href = "https://www.google.com/recaptcha/admin">https://www.google.com/recaptcha/admin</a></kbd> to generate them. Edit this file and set the respective keys in <kbd>$siteKey</kbd> and <kbd>$secret</kbd>. Reload the page after this.</p> | |
+ <?php | |
+else: | |
+ // Add the g-recaptcha tag to the form you want to include the reCAPTCHA element | |
+ ?> | |
+ <p>reCAPTCHA will provide a score for this request.</p> | |
+ <ol id="recaptcha-steps"> | |
+ <li class="step0">reCAPTCHA script loading</li> | |
+ <li style="display:none" class="step1"><kbd>grecaptcha.ready()</kbd> fired, calling <pre>grecaptcha.execute('<?php echo $siteKey; ?>', {action: 'homepage'})'</pre></li> | |
+ <li style="display:none" class="step2">Received token from reCAPTCHA service, sending to our backend with <kbd>fetch('/recaptcha-v3-verify.php?token='+<span class="token">123</span>)</kbd></li> | |
+ <li style="display:none" class="step3">Received response from our backend: <pre class="response">response</pre></li> | |
+ </ol> | |
+ <p><a href="/recaptcha-v3-request-scores.php">⟳ Try again</a></p> | |
+ | |
+ <script src="https://www.google.com/recaptcha/api.js?render=<?php echo $siteKey; ?>"></script> | |
+ <script> | |
+ const steps = document.getElementById('recaptcha-steps'); | |
+ grecaptcha.ready(function() { | |
+ document.querySelector('.step1').style.display = 'list-item'; | |
+ grecaptcha.execute('<?php echo $siteKey; ?>', {action: 'homepage'}).then(function(token) { | |
+ document.querySelector('.token').innerHTML = token; | |
+ document.querySelector('.step2').style.display = 'list-item'; | |
+ | |
+ fetch('/recaptcha-v3-verify.php?token='+token).then(function(response) { | |
+ response.json().then(function(data) { | |
+ document.querySelector('.response').innerHTML = JSON.stringify(data, null, 2); | |
+ document.querySelector('.step3').style.display = 'list-item'; | |
+ }); | |
+ }); | |
+ }); | |
+ }); | |
+</script> | |
+ <?php | |
+endif;?> | |
+</main> | |
+ | |
+<!-- Google Analytics - just ignore this --> | |
+<script async src="https://www.googletagmanager.com/gtag/js?id=UA-123057962-1"></script> | |
+<script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-123057962-1');</script> | |
diff --git a/recaptcha-php/examples/recaptcha-v3-verify.php b/recaptcha-php/examples/recaptcha-v3-verify.php | |
new file mode 100644 | |
index 0000000..31c574c | |
--- /dev/null | |
+++ b/recaptcha-php/examples/recaptcha-v3-verify.php | |
@@ -0,0 +1,49 @@ | |
+<?php | |
+/** | |
+ * @copyright Copyright (c) 2015, Google Inc. | |
+ * @link https://www.google.com/recaptcha | |
+ * | |
+ * Permission is hereby granted, free of charge, to any person obtaining a copy | |
+ * of this software and associated documentation files (the "Software"), to deal | |
+ * in the Software without restriction, including without limitation the rights | |
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
+ * copies of the Software, and to permit persons to whom the Software is | |
+ * furnished to do so, subject to the following conditions: | |
+ * | |
+ * The above copyright notice and this permission notice shall be included in | |
+ * all copies or substantial portions of the Software. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
+ * THE SOFTWARE. | |
+ */ | |
+require __DIR__ . '/appengine-https.php'; | |
+ | |
+// Initiate the autoloader. The file should be generated by Composer. | |
+// You will provide your own autoloader or require the files directly if you did | |
+// not install via Composer. | |
+require_once __DIR__ . '/../vendor/autoload.php'; | |
+ | |
+// Register API keys at https://www.google.com/recaptcha/admin | |
+$siteKey = ''; | |
+$secret = ''; | |
+ | |
+// Copy the config.php.dist file to config.php and update it with your keys to run the examples | |
+if ($siteKey == '' && is_readable(__DIR__ . '/config.php')) { | |
+ $config = include __DIR__ . '/config.php'; | |
+ $siteKey = $config['v3']['site']; | |
+ $secret = $config['v3']['secret']; | |
+} | |
+ | |
+// Effectively we're providing an API endpoint here that will accept the token, verify it, and return the action / score to the page | |
+$recaptcha = new \ReCaptcha\ReCaptcha($secret); | |
+$resp = $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME']) | |
+ ->setExpectedAction('homepage') | |
+ ->setScoreThreshold(0.5) | |
+ ->verify($_GET['token'], $_SERVER['REMOTE_ADDR']); | |
+header('Content-type:application/json'); | |
+echo json_encode($resp->toArray()); | |
diff --git a/recaptcha-php/examples/robots.txt b/recaptcha-php/examples/robots.txt | |
new file mode 100644 | |
index 0000000..eb05362 | |
--- /dev/null | |
+++ b/recaptcha-php/examples/robots.txt | |
@@ -0,0 +1,2 @@ | |
+User-agent: * | |
+Disallow: | |
diff --git a/recaptcha-php/phpunit.xml.dist b/recaptcha-php/phpunit.xml.dist | |
index 4376678..ae86610 100644 | |
--- a/recaptcha-php/phpunit.xml.dist | |
+++ b/recaptcha-php/phpunit.xml.dist | |
@@ -14,4 +14,7 @@ | |
<directory suffix=".php">src/ReCaptcha/</directory> | |
</whitelist> | |
</filter> | |
+ <logging> | |
+ <log type="coverage-clover" target="build/logs/clover.xml"/> | |
+ </logging> | |
</phpunit> | |
diff --git a/recaptcha-php/src/ReCaptcha/ReCaptcha.php b/recaptcha-php/src/ReCaptcha/ReCaptcha.php | |
index bda769c..8939e84 100644 | |
--- a/recaptcha-php/src/ReCaptcha/ReCaptcha.php | |
+++ b/recaptcha-php/src/ReCaptcha/ReCaptcha.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -35,7 +35,73 @@ class ReCaptcha | |
* Version of this client library. | |
* @const string | |
*/ | |
- const VERSION = 'php_1.1.3'; | |
+ const VERSION = 'php_1.2.1'; | |
+ | |
+ /** | |
+ * URL for reCAPTCHA sitevrerify API | |
+ * @const string | |
+ */ | |
+ const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; | |
+ | |
+ /** | |
+ * Invalid JSON received | |
+ * @const string | |
+ */ | |
+ const E_INVALID_JSON = 'invalid-json'; | |
+ | |
+ /** | |
+ * Could not connect to service | |
+ * @const string | |
+ */ | |
+ const E_CONNECTION_FAILED = 'connection-failed'; | |
+ | |
+ /** | |
+ * Did not receive a 200 from the service | |
+ * @const string | |
+ */ | |
+ const E_BAD_RESPONSE = 'bad-response'; | |
+ | |
+ /** | |
+ * Not a success, but no error codes received! | |
+ * @const string | |
+ */ | |
+ const E_UNKNOWN_ERROR = 'unknown-error'; | |
+ | |
+ /** | |
+ * ReCAPTCHA response not provided | |
+ * @const string | |
+ */ | |
+ const E_MISSING_INPUT_RESPONSE = 'missing-input-response'; | |
+ | |
+ /** | |
+ * Expected hostname did not match | |
+ * @const string | |
+ */ | |
+ const E_HOSTNAME_MISMATCH = 'hostname-mismatch'; | |
+ | |
+ /** | |
+ * Expected APK package name did not match | |
+ * @const string | |
+ */ | |
+ const E_APK_PACKAGE_NAME_MISMATCH = 'apk_package_name-mismatch'; | |
+ | |
+ /** | |
+ * Expected action did not match | |
+ * @const string | |
+ */ | |
+ const E_ACTION_MISMATCH = 'action-mismatch'; | |
+ | |
+ /** | |
+ * Score threshold not met | |
+ * @const string | |
+ */ | |
+ const E_SCORE_THRESHOLD_NOT_MET = 'score-threshold-not-met'; | |
+ | |
+ /** | |
+ * Challenge timeout | |
+ * @const string | |
+ */ | |
+ const E_CHALLENGE_TIMEOUT = 'challenge-timeout'; | |
/** | |
* Shared secret for the site. | |
@@ -52,7 +118,7 @@ class ReCaptcha | |
/** | |
* Create a configured instance to use the reCAPTCHA service. | |
* | |
- * @param string $secret shared secret between site and reCAPTCHA server. | |
+ * @param string $secret The shared key between your site and reCAPTCHA. | |
* @param RequestMethod $requestMethod method used to send the request. Defaults to POST. | |
* @throws \RuntimeException if $secret is invalid | |
*/ | |
@@ -67,19 +133,14 @@ class ReCaptcha | |
} | |
$this->secret = $secret; | |
- | |
- if (!is_null($requestMethod)) { | |
- $this->requestMethod = $requestMethod; | |
- } else { | |
- $this->requestMethod = new RequestMethod\Post(); | |
- } | |
+ $this->requestMethod = (is_null($requestMethod)) ? new RequestMethod\Post() : $requestMethod; | |
} | |
/** | |
* Calls the reCAPTCHA siteverify API to verify whether the user passes | |
- * CAPTCHA test. | |
+ * CAPTCHA test and additionally runs any specified additional checks | |
* | |
- * @param string $response The value of 'g-recaptcha-response' in the submitted form. | |
+ * @param string $response The user response token provided by reCAPTCHA, verifying the user on your site. | |
* @param string $remoteIp The end user's IP address. | |
* @return Response Response from the service. | |
*/ | |
@@ -87,12 +148,114 @@ class ReCaptcha | |
{ | |
// Discard empty solution submissions | |
if (empty($response)) { | |
- $recaptchaResponse = new Response(false, array('missing-input-response')); | |
+ $recaptchaResponse = new Response(false, array(self::E_MISSING_INPUT_RESPONSE)); | |
return $recaptchaResponse; | |
} | |
$params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION); | |
$rawResponse = $this->requestMethod->submit($params); | |
- return Response::fromJson($rawResponse); | |
+ $initialResponse = Response::fromJson($rawResponse); | |
+ $validationErrors = array(); | |
+ | |
+ if (isset($this->hostname) && strcasecmp($this->hostname, $initialResponse->getHostname()) !== 0) { | |
+ $validationErrors[] = self::E_HOSTNAME_MISMATCH; | |
+ } | |
+ | |
+ if (isset($this->apkPackageName) && strcasecmp($this->apkPackageName, $initialResponse->getApkPackageName()) !== 0) { | |
+ $validationErrors[] = self::E_APK_PACKAGE_NAME_MISMATCH; | |
+ } | |
+ | |
+ if (isset($this->action) && strcasecmp($this->action, $initialResponse->getAction()) !== 0) { | |
+ $validationErrors[] = self::E_ACTION_MISMATCH; | |
+ } | |
+ | |
+ if (isset($this->threshold) && $this->threshold > $initialResponse->getScore()) { | |
+ $validationErrors[] = self::E_SCORE_THRESHOLD_NOT_MET; | |
+ } | |
+ | |
+ if (isset($this->timeoutSeconds)) { | |
+ $challengeTs = strtotime($initialResponse->getChallengeTs()); | |
+ | |
+ if ($challengeTs > 0 && time() - $challengeTs > $this->timeoutSeconds) { | |
+ $validationErrors[] = self::E_CHALLENGE_TIMEOUT; | |
+ } | |
+ } | |
+ | |
+ if (empty($validationErrors)) { | |
+ return $initialResponse; | |
+ } | |
+ | |
+ return new Response( | |
+ false, | |
+ array_merge($initialResponse->getErrorCodes(), $validationErrors), | |
+ $initialResponse->getHostname(), | |
+ $initialResponse->getChallengeTs(), | |
+ $initialResponse->getApkPackageName(), | |
+ $initialResponse->getScore(), | |
+ $initialResponse->getAction() | |
+ ); | |
+ } | |
+ | |
+ /** | |
+ * Provide a hostname to match against in verify() | |
+ * This should be without a protocol or trailing slash, e.g. www.google.com | |
+ * | |
+ * @param string $hostname Expected hostname | |
+ * @return ReCaptcha Current instance for fluent interface | |
+ */ | |
+ public function setExpectedHostname($hostname) | |
+ { | |
+ $this->hostname = $hostname; | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Provide an APK package name to match against in verify() | |
+ * | |
+ * @param string $apkPackageName Expected APK package name | |
+ * @return ReCaptcha Current instance for fluent interface | |
+ */ | |
+ public function setExpectedApkPackageName($apkPackageName) | |
+ { | |
+ $this->apkPackageName = $apkPackageName; | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Provide an action to match against in verify() | |
+ * This should be set per page. | |
+ * | |
+ * @param string $action Expected action | |
+ * @return ReCaptcha Current instance for fluent interface | |
+ */ | |
+ public function setExpectedAction($action) | |
+ { | |
+ $this->action = $action; | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Provide a threshold to meet or exceed in verify() | |
+ * Threshold should be a float between 0 and 1 which will be tested as response >= threshold. | |
+ * | |
+ * @param float $threshold Expected threshold | |
+ * @return ReCaptcha Current instance for fluent interface | |
+ */ | |
+ public function setScoreThreshold($threshold) | |
+ { | |
+ $this->threshold = floatval($threshold); | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Provide a timeout in seconds to test against the challenge timestamp in verify() | |
+ * | |
+ * @param int $timeoutSeconds Expected hostname | |
+ * @return ReCaptcha Current instance for fluent interface | |
+ */ | |
+ public function setChallengeTimeout($timeoutSeconds) | |
+ { | |
+ $this->timeoutSeconds = $timeoutSeconds; | |
+ return $this; | |
} | |
} | |
diff --git a/recaptcha-php/src/ReCaptcha/RequestMethod.php b/recaptcha-php/src/ReCaptcha/RequestMethod.php | |
index fc4dde5..2fd94b3 100644 | |
--- a/recaptcha-php/src/ReCaptcha/RequestMethod.php | |
+++ b/recaptcha-php/src/ReCaptcha/RequestMethod.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
diff --git a/recaptcha-php/src/ReCaptcha/RequestMethod/Curl.php b/recaptcha-php/src/ReCaptcha/RequestMethod/Curl.php | |
index 20ee530..3d8dddd 100644 | |
--- a/recaptcha-php/src/ReCaptcha/RequestMethod/Curl.php | |
+++ b/recaptcha-php/src/ReCaptcha/RequestMethod/Curl.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
diff --git a/recaptcha-php/src/ReCaptcha/RequestMethod/CurlPost.php b/recaptcha-php/src/ReCaptcha/RequestMethod/CurlPost.php | |
index e06d1cc..59886f8 100644 | |
--- a/recaptcha-php/src/ReCaptcha/RequestMethod/CurlPost.php | |
+++ b/recaptcha-php/src/ReCaptcha/RequestMethod/CurlPost.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -26,6 +26,7 @@ | |
namespace ReCaptcha\RequestMethod; | |
+use ReCaptcha\ReCaptcha; | |
use ReCaptcha\RequestMethod; | |
use ReCaptcha\RequestParameters; | |
@@ -36,25 +37,28 @@ use ReCaptcha\RequestParameters; | |
*/ | |
class CurlPost implements RequestMethod | |
{ | |
- /** | |
- * URL to which requests are sent via cURL. | |
- * @const string | |
- */ | |
- const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; | |
- | |
/** | |
* Curl connection to the reCAPTCHA service | |
* @var Curl | |
*/ | |
private $curl; | |
- public function __construct(Curl $curl = null) | |
+ /** | |
+ * URL for reCAPTCHA sitevrerify API | |
+ * @var string | |
+ */ | |
+ private $siteVerifyUrl; | |
+ | |
+ /** | |
+ * Only needed if you want to override the defaults | |
+ * | |
+ * @param Curl $curl Curl resource | |
+ * @param string $siteVerifyUrl URL for reCAPTCHA sitevrerify API | |
+ */ | |
+ public function __construct(Curl $curl = null, $siteVerifyUrl = null) | |
{ | |
- if (!is_null($curl)) { | |
- $this->curl = $curl; | |
- } else { | |
- $this->curl = new Curl(); | |
- } | |
+ $this->curl = (is_null($curl)) ? new Curl() : $curl; | |
+ $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; | |
} | |
/** | |
@@ -65,7 +69,7 @@ class CurlPost implements RequestMethod | |
*/ | |
public function submit(RequestParameters $params) | |
{ | |
- $handle = $this->curl->init(self::SITE_VERIFY_URL); | |
+ $handle = $this->curl->init($this->siteVerifyUrl); | |
$options = array( | |
CURLOPT_POST => true, | |
@@ -83,6 +87,10 @@ class CurlPost implements RequestMethod | |
$response = $this->curl->exec($handle); | |
$this->curl->close($handle); | |
- return $response; | |
+ if ($response !== false) { | |
+ return $response; | |
+ } | |
+ | |
+ return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; | |
} | |
} | |
diff --git a/recaptcha-php/src/ReCaptcha/RequestMethod/Post.php b/recaptcha-php/src/ReCaptcha/RequestMethod/Post.php | |
index 01ab33b..9e26582 100644 | |
--- a/recaptcha-php/src/ReCaptcha/RequestMethod/Post.php | |
+++ b/recaptcha-php/src/ReCaptcha/RequestMethod/Post.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -26,6 +26,7 @@ | |
namespace ReCaptcha\RequestMethod; | |
+use ReCaptcha\ReCaptcha; | |
use ReCaptcha\RequestMethod; | |
use ReCaptcha\RequestParameters; | |
@@ -35,10 +36,20 @@ use ReCaptcha\RequestParameters; | |
class Post implements RequestMethod | |
{ | |
/** | |
- * URL to which requests are POSTed. | |
- * @const string | |
+ * URL for reCAPTCHA sitevrerify API | |
+ * @var string | |
*/ | |
- const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; | |
+ private $siteVerifyUrl; | |
+ | |
+ /** | |
+ * Only needed if you want to override the defaults | |
+ * | |
+ * @param string $siteVerifyUrl URL for reCAPTCHA sitevrerify API | |
+ */ | |
+ public function __construct($siteVerifyUrl = null) | |
+ { | |
+ $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; | |
+ } | |
/** | |
* Submit the POST request with the specified parameters. | |
@@ -48,11 +59,6 @@ class Post implements RequestMethod | |
*/ | |
public function submit(RequestParameters $params) | |
{ | |
- /** | |
- * 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'; | |
$options = array( | |
'http' => array( | |
'header' => "Content-type: application/x-www-form-urlencoded\r\n", | |
@@ -60,11 +66,15 @@ class Post implements RequestMethod | |
'content' => $params->toQueryString(), | |
// 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', | |
), | |
); | |
$context = stream_context_create($options); | |
- return file_get_contents(self::SITE_VERIFY_URL, false, $context); | |
+ $response = file_get_contents($this->siteVerifyUrl, false, $context); | |
+ | |
+ if ($response !== false) { | |
+ return $response; | |
+ } | |
+ | |
+ return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; | |
} | |
} | |
diff --git a/recaptcha-php/src/ReCaptcha/RequestMethod/Socket.php b/recaptcha-php/src/ReCaptcha/RequestMethod/Socket.php | |
index f51f123..12322e8 100644 | |
--- a/recaptcha-php/src/ReCaptcha/RequestMethod/Socket.php | |
+++ b/recaptcha-php/src/ReCaptcha/RequestMethod/Socket.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -36,7 +36,7 @@ class Socket | |
/** | |
* fsockopen | |
- * | |
+ * | |
* @see http://php.net/fsockopen | |
* @param string $hostname | |
* @param int $port | |
@@ -57,7 +57,7 @@ class Socket | |
/** | |
* fwrite | |
- * | |
+ * | |
* @see http://php.net/fwrite | |
* @param string $string | |
* @param int $length | |
@@ -70,7 +70,7 @@ class Socket | |
/** | |
* fgets | |
- * | |
+ * | |
* @see http://php.net/fgets | |
* @param int $length | |
* @return string | |
@@ -82,7 +82,7 @@ class Socket | |
/** | |
* feof | |
- * | |
+ * | |
* @see http://php.net/feof | |
* @return bool | |
*/ | |
@@ -93,7 +93,7 @@ class Socket | |
/** | |
* fclose | |
- * | |
+ * | |
* @see http://php.net/fclose | |
* @return bool | |
*/ | |
diff --git a/recaptcha-php/src/ReCaptcha/RequestMethod/SocketPost.php b/recaptcha-php/src/ReCaptcha/RequestMethod/SocketPost.php | |
index 45bee0e..ca1ca90 100644 | |
--- a/recaptcha-php/src/ReCaptcha/RequestMethod/SocketPost.php | |
+++ b/recaptcha-php/src/ReCaptcha/RequestMethod/SocketPost.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -26,6 +26,7 @@ | |
namespace ReCaptcha\RequestMethod; | |
+use ReCaptcha\ReCaptcha; | |
use ReCaptcha\RequestMethod; | |
use ReCaptcha\RequestParameters; | |
@@ -36,27 +37,6 @@ use ReCaptcha\RequestParameters; | |
*/ | |
class SocketPost implements RequestMethod | |
{ | |
- /** | |
- * reCAPTCHA service host. | |
- * @const string | |
- */ | |
- const RECAPTCHA_HOST = 'www.google.com'; | |
- | |
- /** | |
- * @const string reCAPTCHA service path | |
- */ | |
- const SITE_VERIFY_PATH = '/recaptcha/api/siteverify'; | |
- | |
- /** | |
- * @const string Bad request error | |
- */ | |
- const BAD_REQUEST = '{"success": false, "error-codes": ["invalid-request"]}'; | |
- | |
- /** | |
- * @const string Bad response error | |
- */ | |
- const BAD_RESPONSE = '{"success": false, "error-codes": ["invalid-response"]}'; | |
- | |
/** | |
* Socket to the reCAPTCHA service | |
* @var Socket | |
@@ -64,17 +44,15 @@ class SocketPost implements RequestMethod | |
private $socket; | |
/** | |
- * Constructor | |
+ * Only needed if you want to override the defaults | |
* | |
* @param \ReCaptcha\RequestMethod\Socket $socket optional socket, injectable for testing | |
+ * @param string $siteVerifyUrl URL for reCAPTCHA sitevrerify API | |
*/ | |
- public function __construct(Socket $socket = null) | |
+ public function __construct(Socket $socket = null, $siteVerifyUrl = null) | |
{ | |
- if (!is_null($socket)) { | |
- $this->socket = $socket; | |
- } else { | |
- $this->socket = new Socket(); | |
- } | |
+ $this->socket = (is_null($socket)) ? new Socket() : $socket; | |
+ $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; | |
} | |
/** | |
@@ -87,15 +65,16 @@ class SocketPost implements RequestMethod | |
{ | |
$errno = 0; | |
$errstr = ''; | |
+ $urlParsed = parse_url($this->siteVerifyUrl); | |
- if (false === $this->socket->fsockopen('ssl://' . self::RECAPTCHA_HOST, 443, $errno, $errstr, 30)) { | |
- return self::BAD_REQUEST; | |
+ if (false === $this->socket->fsockopen('ssl://' . $urlParsed['host'], 443, $errno, $errstr, 30)) { | |
+ return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; | |
} | |
$content = $params->toQueryString(); | |
- $request = "POST " . self::SITE_VERIFY_PATH . " HTTP/1.1\r\n"; | |
- $request .= "Host: " . self::RECAPTCHA_HOST . "\r\n"; | |
+ $request = "POST " . $urlParsed['path'] . " HTTP/1.1\r\n"; | |
+ $request .= "Host: " . $urlParsed['host'] . "\r\n"; | |
$request .= "Content-Type: application/x-www-form-urlencoded\r\n"; | |
$request .= "Content-length: " . strlen($content) . "\r\n"; | |
$request .= "Connection: close\r\n\r\n"; | |
@@ -111,7 +90,7 @@ class SocketPost implements RequestMethod | |
$this->socket->fclose(); | |
if (0 !== strpos($response, 'HTTP/1.1 200 OK')) { | |
- return self::BAD_RESPONSE; | |
+ return '{"success": false, "error-codes": ["'.ReCaptcha::E_BAD_RESPONSE.'"]}'; | |
} | |
$parts = preg_split("#\n\s*\n#Uis", $response); | |
diff --git a/recaptcha-php/src/ReCaptcha/RequestParameters.php b/recaptcha-php/src/ReCaptcha/RequestParameters.php | |
index cb66f26..b6dd998 100644 | |
--- a/recaptcha-php/src/ReCaptcha/RequestParameters.php | |
+++ b/recaptcha-php/src/ReCaptcha/RequestParameters.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -32,13 +32,13 @@ namespace ReCaptcha; | |
class RequestParameters | |
{ | |
/** | |
- * Site secret. | |
+ * The shared key between your site and reCAPTCHA. | |
* @var string | |
*/ | |
private $secret; | |
/** | |
- * Form response. | |
+ * The user response token provided by reCAPTCHA, verifying the user on your site. | |
* @var string | |
*/ | |
private $response; | |
diff --git a/recaptcha-php/src/ReCaptcha/Response.php b/recaptcha-php/src/ReCaptcha/Response.php | |
index 4d4d036..5c15c37 100644 | |
--- a/recaptcha-php/src/ReCaptcha/Response.php | |
+++ b/recaptcha-php/src/ReCaptcha/Response.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -49,6 +49,30 @@ class Response | |
*/ | |
private $hostname; | |
+ /** | |
+ * Timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ) | |
+ * @var string | |
+ */ | |
+ private $challengeTs; | |
+ | |
+ /** | |
+ * APK package name | |
+ * @var string | |
+ */ | |
+ private $apkPackageName; | |
+ | |
+ /** | |
+ * Score assigned to the request | |
+ * @var float | |
+ */ | |
+ private $score; | |
+ | |
+ /** | |
+ * Action as specified by the page | |
+ * @var string | |
+ */ | |
+ private $action; | |
+ | |
/** | |
* Build the response from the expected JSON returned by the service. | |
* | |
@@ -60,34 +84,46 @@ class Response | |
$responseData = json_decode($json, true); | |
if (!$responseData) { | |
- return new Response(false, array('invalid-json')); | |
+ return new Response(false, array(ReCaptcha::E_INVALID_JSON)); | |
} | |
$hostname = isset($responseData['hostname']) ? $responseData['hostname'] : null; | |
+ $challengeTs = isset($responseData['challenge_ts']) ? $responseData['challenge_ts'] : null; | |
+ $apkPackageName = isset($responseData['apk_package_name']) ? $responseData['apk_package_name'] : null; | |
+ $score = isset($responseData['score']) ? floatval($responseData['score']) : null; | |
+ $action = isset($responseData['action']) ? $responseData['action'] : null; | |
if (isset($responseData['success']) && $responseData['success'] == true) { | |
- return new Response(true, array(), $hostname); | |
+ return new Response(true, array(), $hostname, $challengeTs, $apkPackageName, $score, $action); | |
} | |
if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) { | |
- return new Response(false, $responseData['error-codes'], $hostname); | |
+ return new Response(false, $responseData['error-codes'], $hostname, $challengeTs, $apkPackageName, $score, $action); | |
} | |
- return new Response(false, array(), $hostname); | |
+ return new Response(false, array(ReCaptcha::E_UNKNOWN_ERROR), $hostname, $challengeTs, $apkPackageName, $score, $action); | |
} | |
/** | |
* Constructor. | |
* | |
* @param boolean $success | |
- * @param array $errorCodes | |
* @param string $hostname | |
+ * @param string $challengeTs | |
+ * @param string $apkPackageName | |
+ * @param float $score | |
+ * @param strong $action | |
+ * @param array $errorCodes | |
*/ | |
- public function __construct($success, array $errorCodes = array(), $hostname = null) | |
+ public function __construct($success, array $errorCodes = array(), $hostname = null, $challengeTs = null, $apkPackageName = null, $score = null, $action = null) | |
{ | |
$this->success = $success; | |
- $this->errorCodes = $errorCodes; | |
$this->hostname = $hostname; | |
+ $this->challengeTs = $challengeTs; | |
+ $this->apkPackageName = $apkPackageName; | |
+ $this->score = $score; | |
+ $this->action = $action; | |
+ $this->errorCodes = $errorCodes; | |
} | |
/** | |
@@ -117,6 +153,58 @@ class Response | |
*/ | |
public function getHostname() | |
{ | |
- return $this->hostname; | |
+ return $this->hostname; | |
+ } | |
+ | |
+ /** | |
+ * Get challenge timestamp | |
+ * | |
+ * @return string | |
+ */ | |
+ public function getChallengeTs() | |
+ { | |
+ return $this->challengeTs; | |
+ } | |
+ | |
+ /** | |
+ * Get APK package name | |
+ * | |
+ * @return string | |
+ */ | |
+ public function getApkPackageName() | |
+ { | |
+ return $this->apkPackageName; | |
+ } | |
+ /** | |
+ * Get score | |
+ * | |
+ * @return float | |
+ */ | |
+ public function getScore() | |
+ { | |
+ return $this->score; | |
+ } | |
+ | |
+ /** | |
+ * Get action | |
+ * | |
+ * @return string | |
+ */ | |
+ public function getAction() | |
+ { | |
+ return $this->action; | |
+ } | |
+ | |
+ public function toArray() | |
+ { | |
+ return array( | |
+ 'success' => $this->isSuccess(), | |
+ 'hostname' => $this->getHostname(), | |
+ 'challenge_ts' => $this->getChallengeTs(), | |
+ 'apk_package_name' => $this->getApkPackageName(), | |
+ 'score' => $this->getScore(), | |
+ 'action' => $this->getAction(), | |
+ 'error-codes' => $this->getErrorCodes(), | |
+ ); | |
} | |
} | |
diff --git a/recaptcha-php/src/autoload.php b/recaptcha-php/src/autoload.php | |
index 5a7ee94..95e249e 100644 | |
--- a/recaptcha-php/src/autoload.php | |
+++ b/recaptcha-php/src/autoload.php | |
@@ -7,10 +7,10 @@ | |
spl_autoload_register(function ($class) { | |
if (substr($class, 0, 10) !== 'ReCaptcha\\') { | |
- /* If the class does not lie under the "ReCaptcha" namespace, | |
- * then we can exit immediately. | |
- */ | |
- return; | |
+ /* If the class does not lie under the "ReCaptcha" namespace, | |
+ * then we can exit immediately. | |
+ */ | |
+ return; | |
} | |
/* All of the classes have names like "ReCaptcha\Foo", so we need | |
@@ -26,6 +26,8 @@ spl_autoload_register(function ($class) { | |
$path = dirname(__FILE__).'/'.$class.'.php'; | |
if (is_readable($path)) { | |
require_once $path; | |
+ | |
+ return; | |
} | |
/* If we didn't find what we're looking for already, maybe it's | |
diff --git a/recaptcha-php/tests/ReCaptcha/ReCaptchaTest.php b/recaptcha-php/tests/ReCaptcha/ReCaptchaTest.php | |
index dbbfc99..d3adb54 100644 | |
--- a/recaptcha-php/tests/ReCaptcha/ReCaptchaTest.php | |
+++ b/recaptcha-php/tests/ReCaptcha/ReCaptchaTest.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -26,7 +26,9 @@ | |
namespace ReCaptcha; | |
-class ReCaptchaTest extends \PHPUnit_Framework_TestCase | |
+use PHPUnit\Framework\TestCase; | |
+ | |
+class ReCaptchaTest extends TestCase | |
{ | |
/** | |
@@ -54,22 +56,135 @@ class ReCaptchaTest extends \PHPUnit_Framework_TestCase | |
$rc = new ReCaptcha('secret'); | |
$response = $rc->verify(''); | |
$this->assertFalse($response->isSuccess()); | |
- $this->assertEquals(array('missing-input-response'), $response->getErrorCodes()); | |
+ $this->assertEquals(array(Recaptcha::E_MISSING_INPUT_RESPONSE), $response->getErrorCodes()); | |
} | |
- public function testVerifyReturnsResponse() | |
+ private function getMockRequestMethod($responseJson) | |
{ | |
- $method = $this->getMock('\\ReCaptcha\\RequestMethod', array('submit')); | |
- $method->expects($this->once()) | |
- ->method('submit') | |
- ->with($this->callback(function ($params) { | |
+ $method = $this->getMockBuilder(\ReCaptcha\RequestMethod::class) | |
+ ->disableOriginalConstructor() | |
+ ->setMethods(array('submit')) | |
+ ->getMock(); | |
+ $method->expects($this->any()) | |
+ ->method('submit') | |
+ ->with($this->callback(function ($params) { | |
+ return true; | |
+ })) | |
+ ->will($this->returnValue($responseJson)); | |
+ return $method; | |
+ } | |
- return true; | |
- })) | |
- ->will($this->returnValue('{"success": true}')); | |
- ; | |
+ public function testVerifyReturnsResponse() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true}'); | |
$rc = new ReCaptcha('secret', $method); | |
$response = $rc->verify('response'); | |
$this->assertTrue($response->isSuccess()); | |
} | |
+ | |
+ public function testVerifyReturnsInitialResponseWithoutAdditionalChecks() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $initialResponse = $rc->verify('response'); | |
+ $this->assertEquals($initialResponse, $rc->verify('response')); | |
+ } | |
+ | |
+ public function testVerifyHostnameMatch() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true, "hostname": "host.name"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setExpectedHostname('host.name')->verify('response'); | |
+ $this->assertTrue($response->isSuccess()); | |
+ } | |
+ | |
+ public function testVerifyHostnameMisMatch() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true, "hostname": "host.NOTname"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setExpectedHostname('host.name')->verify('response'); | |
+ $this->assertFalse($response->isSuccess()); | |
+ $this->assertEquals(array(ReCaptcha::E_HOSTNAME_MISMATCH), $response->getErrorCodes()); | |
+ } | |
+ | |
+ public function testVerifyApkPackageNameMatch() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true, "apk_package_name": "apk.name"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setExpectedApkPackageName('apk.name')->verify('response'); | |
+ $this->assertTrue($response->isSuccess()); | |
+ } | |
+ | |
+ public function testVerifyApkPackageNameMisMatch() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true, "apk_package_name": "apk.NOTname"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setExpectedApkPackageName('apk.name')->verify('response'); | |
+ $this->assertFalse($response->isSuccess()); | |
+ $this->assertEquals(array(ReCaptcha::E_APK_PACKAGE_NAME_MISMATCH), $response->getErrorCodes()); | |
+ } | |
+ | |
+ public function testVerifyActionMatch() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true, "action": "action/name"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setExpectedAction('action/name')->verify('response'); | |
+ $this->assertTrue($response->isSuccess()); | |
+ } | |
+ | |
+ public function testVerifyActionMisMatch() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true, "action": "action/NOTname"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setExpectedAction('action/name')->verify('response'); | |
+ $this->assertFalse($response->isSuccess()); | |
+ $this->assertEquals(array(ReCaptcha::E_ACTION_MISMATCH), $response->getErrorCodes()); | |
+ } | |
+ | |
+ public function testVerifyAboveThreshold() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true, "score": "0.9"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setScoreThreshold('0.5')->verify('response'); | |
+ $this->assertTrue($response->isSuccess()); | |
+ } | |
+ | |
+ public function testVerifyBelowThreshold() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": true, "score": "0.1"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setScoreThreshold('0.5')->verify('response'); | |
+ $this->assertFalse($response->isSuccess()); | |
+ $this->assertEquals(array(ReCaptcha::E_SCORE_THRESHOLD_NOT_MET), $response->getErrorCodes()); | |
+ } | |
+ | |
+ public function testVerifyWithinTimeout() | |
+ { | |
+ // Responses come back like 2018-07-31T13:48:41Z | |
+ $challengeTs = date('Y-M-d\TH:i:s\Z', time()); | |
+ $method = $this->getMockRequestMethod('{"success": true, "challenge_ts": "'.$challengeTs.'"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setChallengeTimeout('1000')->verify('response'); | |
+ $this->assertTrue($response->isSuccess()); | |
+ } | |
+ | |
+ public function testVerifyOverTimeout() | |
+ { | |
+ // Responses come back like 2018-07-31T13:48:41Z | |
+ $challengeTs = date('Y-M-d\TH:i:s\Z', time() - 600); | |
+ $method = $this->getMockRequestMethod('{"success": true, "challenge_ts": "'.$challengeTs.'"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setChallengeTimeout('60')->verify('response'); | |
+ $this->assertFalse($response->isSuccess()); | |
+ $this->assertEquals(array(ReCaptcha::E_CHALLENGE_TIMEOUT), $response->getErrorCodes()); | |
+ } | |
+ | |
+ public function testVerifyMergesErrors() | |
+ { | |
+ $method = $this->getMockRequestMethod('{"success": false, "error-codes": ["initial-error"], "score": "0.1"}'); | |
+ $rc = new ReCaptcha('secret', $method); | |
+ $response = $rc->setScoreThreshold('0.5')->verify('response'); | |
+ $this->assertFalse($response->isSuccess()); | |
+ $this->assertEquals(array('initial-error', ReCaptcha::E_SCORE_THRESHOLD_NOT_MET), $response->getErrorCodes()); | |
+ } | |
} | |
diff --git a/recaptcha-php/tests/ReCaptcha/RequestMethod/CurlPostTest.php b/recaptcha-php/tests/ReCaptcha/RequestMethod/CurlPostTest.php | |
index 892e207..a0f4e97 100644 | |
--- a/recaptcha-php/tests/ReCaptcha/RequestMethod/CurlPostTest.php | |
+++ b/recaptcha-php/tests/ReCaptcha/RequestMethod/CurlPostTest.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -26,11 +26,12 @@ | |
namespace ReCaptcha\RequestMethod; | |
+use \ReCaptcha\ReCaptcha; | |
use \ReCaptcha\RequestParameters; | |
+use PHPUnit\Framework\TestCase; | |
-class CurlPostTest extends \PHPUnit_Framework_TestCase | |
+class CurlPostTest extends TestCase | |
{ | |
- | |
protected function setUp() | |
{ | |
if (!extension_loaded('curl')) { | |
@@ -42,8 +43,10 @@ class CurlPostTest extends \PHPUnit_Framework_TestCase | |
public function testSubmit() | |
{ | |
- $curl = $this->getMock('\\ReCaptcha\\RequestMethod\\Curl', | |
- array('init', 'setoptArray', 'exec', 'close')); | |
+ $curl = $this->getMockBuilder(\ReCaptcha\RequestMethod\Curl::class) | |
+ ->disableOriginalConstructor() | |
+ ->setMethods(array('init', 'setoptArray', 'exec', 'close')) | |
+ ->getMock(); | |
$curl->expects($this->once()) | |
->method('init') | |
->willReturn(new \stdClass); | |
@@ -60,4 +63,53 @@ class CurlPostTest extends \PHPUnit_Framework_TestCase | |
$response = $pc->submit(new RequestParameters("secret", "response")); | |
$this->assertEquals('RESPONSEBODY', $response); | |
} | |
+ | |
+ public function testOverrideSiteVerifyUrl() | |
+ { | |
+ $url = 'OVERRIDE'; | |
+ | |
+ $curl = $this->getMockBuilder(\ReCaptcha\RequestMethod\Curl::class) | |
+ ->disableOriginalConstructor() | |
+ ->setMethods(array('init', 'setoptArray', 'exec', 'close')) | |
+ ->getMock(); | |
+ $curl->expects($this->once()) | |
+ ->method('init') | |
+ ->with($url) | |
+ ->willReturn(new \stdClass); | |
+ $curl->expects($this->once()) | |
+ ->method('setoptArray') | |
+ ->willReturn(true); | |
+ $curl->expects($this->once()) | |
+ ->method('exec') | |
+ ->willReturn('RESPONSEBODY'); | |
+ $curl->expects($this->once()) | |
+ ->method('close'); | |
+ | |
+ $pc = new CurlPost($curl, $url); | |
+ $response = $pc->submit(new RequestParameters("secret", "response")); | |
+ $this->assertEquals('RESPONSEBODY', $response); | |
+ } | |
+ | |
+ public function testConnectionFailureReturnsError() | |
+ { | |
+ $curl = $this->getMockBuilder(\ReCaptcha\RequestMethod\Curl::class) | |
+ ->disableOriginalConstructor() | |
+ ->setMethods(array('init', 'setoptArray', 'exec', 'close')) | |
+ ->getMock(); | |
+ $curl->expects($this->once()) | |
+ ->method('init') | |
+ ->willReturn(new \stdClass); | |
+ $curl->expects($this->once()) | |
+ ->method('setoptArray') | |
+ ->willReturn(true); | |
+ $curl->expects($this->once()) | |
+ ->method('exec') | |
+ ->willReturn(false); | |
+ $curl->expects($this->once()) | |
+ ->method('close'); | |
+ | |
+ $pc = new CurlPost($curl); | |
+ $response = $pc->submit(new RequestParameters("secret", "response")); | |
+ $this->assertEquals('{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}', $response); | |
+ } | |
} | |
diff --git a/recaptcha-php/tests/ReCaptcha/RequestMethod/PostTest.php b/recaptcha-php/tests/ReCaptcha/RequestMethod/PostTest.php | |
index 2eaabb5..1bf7437 100644 | |
--- a/recaptcha-php/tests/ReCaptcha/RequestMethod/PostTest.php | |
+++ b/recaptcha-php/tests/ReCaptcha/RequestMethod/PostTest.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -26,9 +26,11 @@ | |
namespace ReCaptcha\RequestMethod; | |
+use \ReCaptcha\ReCaptcha; | |
use ReCaptcha\RequestParameters; | |
+use PHPUnit\Framework\TestCase; | |
-class PostTest extends \PHPUnit_Framework_TestCase | |
+class PostTest extends TestCase | |
{ | |
public static $assert = null; | |
protected $parameters = null; | |
@@ -36,7 +38,7 @@ class PostTest extends \PHPUnit_Framework_TestCase | |
public function setUp() | |
{ | |
- $this->parameters = new RequestParameters("secret", "response", "remoteip", "version"); | |
+ $this->parameters = new RequestParameters('secret', 'response', 'remoteip', 'version'); | |
} | |
public function tearDown() | |
@@ -47,17 +49,43 @@ class PostTest extends \PHPUnit_Framework_TestCase | |
public function testHTTPContextOptions() | |
{ | |
$req = new Post(); | |
- self::$assert = array($this, "httpContextOptionsCallback"); | |
+ self::$assert = array($this, 'httpContextOptionsCallback'); | |
$req->submit($this->parameters); | |
- $this->assertEquals(1, $this->runcount, "The assertion was ran"); | |
+ $this->assertEquals(1, $this->runcount, 'The assertion was ran'); | |
} | |
public function testSSLContextOptions() | |
{ | |
$req = new Post(); | |
- self::$assert = array($this, "sslContextOptionsCallback"); | |
+ self::$assert = array($this, 'sslContextOptionsCallback'); | |
$req->submit($this->parameters); | |
- $this->assertEquals(1, $this->runcount, "The assertion was ran"); | |
+ $this->assertEquals(1, $this->runcount, 'The assertion was ran'); | |
+ } | |
+ | |
+ public function testOverrideVerifyUrl() | |
+ { | |
+ $req = new Post('https://over.ride/some/path'); | |
+ self::$assert = array($this, 'overrideUrlOptions'); | |
+ $req->submit($this->parameters); | |
+ $this->assertEquals(1, $this->runcount, 'The assertion was ran'); | |
+ } | |
+ | |
+ public function testConnectionFailureReturnsError() | |
+ { | |
+ $req = new Post('https://bad.connection/'); | |
+ self::$assert = array($this, 'connectionFailureResponse'); | |
+ $response = $req->submit($this->parameters); | |
+ $this->assertEquals('{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}', $response); | |
+ } | |
+ | |
+ public function connectionFailureResponse() | |
+ { | |
+ return false; | |
+ } | |
+ public function overrideUrlOptions(array $args) | |
+ { | |
+ $this->runcount++; | |
+ $this->assertEquals('https://over.ride/some/path', $args[0]); | |
} | |
public function httpContextOptionsCallback(array $args) | |
@@ -69,14 +97,14 @@ class PostTest extends \PHPUnit_Framework_TestCase | |
$this->assertArrayHasKey('http', $options); | |
$this->assertArrayHasKey('method', $options['http']); | |
- $this->assertEquals("POST", $options['http']['method']); | |
+ $this->assertEquals('POST', $options['http']['method']); | |
$this->assertArrayHasKey('content', $options['http']); | |
$this->assertEquals($this->parameters->toQueryString(), $options['http']['content']); | |
$this->assertArrayHasKey('header', $options['http']); | |
$headers = array( | |
- "Content-type: application/x-www-form-urlencoded", | |
+ 'Content-type: application/x-www-form-urlencoded', | |
); | |
foreach ($headers as $header) { | |
$this->assertContains($header, $options['http']['header']); | |
@@ -92,19 +120,14 @@ class PostTest extends \PHPUnit_Framework_TestCase | |
$this->assertArrayHasKey('http', $options); | |
$this->assertArrayHasKey('verify_peer', $options['http']); | |
$this->assertTrue($options['http']['verify_peer']); | |
- | |
- $key = version_compare(PHP_VERSION, "5.6.0", "<") ? "CN_name" : "peer_name"; | |
- | |
- $this->assertArrayHasKey($key, $options['http']); | |
- $this->assertEquals("www.google.com", $options['http'][$key]); | |
} | |
protected function assertCommonOptions(array $args) | |
{ | |
$this->assertCount(3, $args); | |
- $this->assertStringStartsWith("https://www.google.com/", $args[0]); | |
+ $this->assertStringStartsWith('https://www.google.com/', $args[0]); | |
$this->assertFalse($args[1]); | |
- $this->assertTrue(is_resource($args[2]), "The context options should be a resource"); | |
+ $this->assertTrue(is_resource($args[2]), 'The context options should be a resource'); | |
} | |
} | |
diff --git a/recaptcha-php/tests/ReCaptcha/RequestMethod/SocketPostTest.php b/recaptcha-php/tests/ReCaptcha/RequestMethod/SocketPostTest.php | |
index 10eb7c2..59970e7 100644 | |
--- a/recaptcha-php/tests/ReCaptcha/RequestMethod/SocketPostTest.php | |
+++ b/recaptcha-php/tests/ReCaptcha/RequestMethod/SocketPostTest.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -26,14 +26,18 @@ | |
namespace ReCaptcha\RequestMethod; | |
+use ReCaptcha\ReCaptcha; | |
use ReCaptcha\RequestParameters; | |
+use PHPUnit\Framework\TestCase; | |
-class SocketPostTest extends \PHPUnit_Framework_TestCase | |
+class SocketPostTest extends TestCase | |
{ | |
- | |
public function testSubmitSuccess() | |
{ | |
- $socket = $this->getMock('\\ReCaptcha\\RequestMethod\\Socket', array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose')); | |
+ $socket = $this->getMockBuilder(\ReCaptcha\RequestMethod\Socket::class) | |
+ ->disableOriginalConstructor() | |
+ ->setMethods(array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose')) | |
+ ->getMock(); | |
$socket->expects($this->once()) | |
->method('fsockopen') | |
->willReturn(true); | |
@@ -54,9 +58,40 @@ class SocketPostTest extends \PHPUnit_Framework_TestCase | |
$this->assertEquals('RESPONSEBODY', $response); | |
} | |
+ public function testOverrideSiteVerifyUrl() | |
+ { | |
+ $socket = $this->getMockBuilder(\ReCaptcha\RequestMethod\Socket::class) | |
+ ->disableOriginalConstructor() | |
+ ->setMethods(array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose')) | |
+ ->getMock(); | |
+ $socket->expects($this->once()) | |
+ ->method('fsockopen') | |
+ ->with('ssl://over.ride', 443, 0, '', 30) | |
+ ->willReturn(true); | |
+ $socket->expects($this->once()) | |
+ ->method('fwrite') | |
+ ->with($this->matchesRegularExpression('/^POST \/some\/path.*Host: over\.ride/s')); | |
+ $socket->expects($this->once()) | |
+ ->method('fgets') | |
+ ->willReturn("HTTP/1.1 200 OK\n\nRESPONSEBODY"); | |
+ $socket->expects($this->exactly(2)) | |
+ ->method('feof') | |
+ ->will($this->onConsecutiveCalls(false, true)); | |
+ $socket->expects($this->once()) | |
+ ->method('fclose') | |
+ ->willReturn(true); | |
+ | |
+ $ps = new SocketPost($socket, 'https://over.ride/some/path'); | |
+ $response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version")); | |
+ $this->assertEquals('RESPONSEBODY', $response); | |
+ } | |
+ | |
public function testSubmitBadResponse() | |
{ | |
- $socket = $this->getMock('\\ReCaptcha\\RequestMethod\\Socket', array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose')); | |
+ $socket = $this->getMockBuilder(\ReCaptcha\RequestMethod\Socket::class) | |
+ ->disableOriginalConstructor() | |
+ ->setMethods(array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose')) | |
+ ->getMock(); | |
$socket->expects($this->once()) | |
->method('fsockopen') | |
->willReturn(true); | |
@@ -74,17 +109,20 @@ class SocketPostTest extends \PHPUnit_Framework_TestCase | |
$ps = new SocketPost($socket); | |
$response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version")); | |
- $this->assertEquals(SocketPost::BAD_RESPONSE, $response); | |
+ $this->assertEquals('{"success": false, "error-codes": ["'.ReCaptcha::E_BAD_RESPONSE.'"]}', $response); | |
} | |
- public function testSubmitBadRequest() | |
+ public function testConnectionFailureReturnsError() | |
{ | |
- $socket = $this->getMock('\\ReCaptcha\\RequestMethod\\Socket', array('fsockopen')); | |
+ $socket = $this->getMockBuilder(\ReCaptcha\RequestMethod\Socket::class) | |
+ ->disableOriginalConstructor() | |
+ ->setMethods(array('fsockopen')) | |
+ ->getMock(); | |
$socket->expects($this->once()) | |
->method('fsockopen') | |
->willReturn(false); | |
$ps = new SocketPost($socket); | |
$response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version")); | |
- $this->assertEquals(SocketPost::BAD_REQUEST, $response); | |
+ $this->assertEquals('{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}', $response); | |
} | |
} | |
diff --git a/recaptcha-php/tests/ReCaptcha/RequestParametersTest.php b/recaptcha-php/tests/ReCaptcha/RequestParametersTest.php | |
index adc601b..697d6fa 100644 | |
--- a/recaptcha-php/tests/ReCaptcha/RequestParametersTest.php | |
+++ b/recaptcha-php/tests/ReCaptcha/RequestParametersTest.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -26,9 +26,10 @@ | |
namespace ReCaptcha; | |
-class RequestParametersTest extends \PHPUnit_Framework_TestCase | |
-{ | |
+use PHPUnit\Framework\TestCase; | |
+class RequestParametersTest extends Testcase | |
+{ | |
public function provideValidData() | |
{ | |
return array( | |
diff --git a/recaptcha-php/tests/ReCaptcha/ResponseTest.php b/recaptcha-php/tests/ReCaptcha/ResponseTest.php | |
index f6075d7..623519e 100644 | |
--- a/recaptcha-php/tests/ReCaptcha/ResponseTest.php | |
+++ b/recaptcha-php/tests/ReCaptcha/ResponseTest.php | |
@@ -3,7 +3,7 @@ | |
* This is a PHP library that handles calling reCAPTCHA. | |
* | |
* @copyright Copyright (c) 2015, Google Inc. | |
- * @link http://www.google.com/recaptcha | |
+ * @link https://www.google.com/recaptcha | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
@@ -26,32 +26,69 @@ | |
namespace ReCaptcha; | |
-class ResponseTest extends \PHPUnit_Framework_TestCase | |
+use PHPUnit\Framework\TestCase; | |
+ | |
+class ResponseTest extends TestCase | |
{ | |
/** | |
* @dataProvider provideJson | |
*/ | |
- public function testFromJson($json, $success, $errorCodes, $hostname) | |
+ public function testFromJson($json, $success, $errorCodes, $hostname, $challengeTs, $apkPackageName, $score, $action) | |
{ | |
$response = Response::fromJson($json); | |
$this->assertEquals($success, $response->isSuccess()); | |
$this->assertEquals($errorCodes, $response->getErrorCodes()); | |
$this->assertEquals($hostname, $response->getHostname()); | |
+ $this->assertEquals($challengeTs, $response->getChallengeTs()); | |
+ $this->assertEquals($apkPackageName, $response->getApkPackageName()); | |
+ $this->assertEquals($score, $response->getScore()); | |
+ $this->assertEquals($action, $response->getAction()); | |
} | |
public function provideJson() | |
{ | |
return array( | |
- array('{"success": true}', true, array(), null), | |
- array('{"success": true, "hostname": "google.com"}', true, array(), 'google.com'), | |
- array('{"success": false, "error-codes": ["test"]}', false, array('test'), null), | |
- array('{"success": false, "error-codes": ["test"], "hostname": "google.com"}', false, array('test'), 'google.com'), | |
- array('{"success": true, "error-codes": ["test"]}', true, array(), null), | |
- array('{"success": true, "error-codes": ["test"], "hostname": "google.com"}', true, array(), 'google.com'), | |
- array('{"success": false}', false, array(), null), | |
- array('{"success": false, "hostname": "google.com"}', false, array(), 'google.com'), | |
- array('BAD JSON', false, array('invalid-json'), null), | |
+ array( | |
+ '{"success": true}', | |
+ true, array(), null, null, null, null, null, | |
+ ), | |
+ array( | |
+ '{"success": true, "hostname": "google.com"}', | |
+ true, array(), 'google.com', null, null, null, null, | |
+ ), | |
+ array( | |
+ '{"success": false, "error-codes": ["test"]}', | |
+ false, array('test'), null, null, null, null, null, | |
+ ), | |
+ array( | |
+ '{"success": false, "error-codes": ["test"], "hostname": "google.com"}', | |
+ false, array('test'), 'google.com', null, null, null, null, | |
+ ), | |
+ array( | |
+ '{"success": false, "error-codes": ["test"], "hostname": "google.com", "challenge_ts": "timestamp", "apk_package_name": "apk", "score": "0.5", "action": "action"}', | |
+ false, array('test'), 'google.com', 'timestamp', 'apk', 0.5, 'action', | |
+ ), | |
+ array( | |
+ '{"success": true, "error-codes": ["test"]}', | |
+ true, array(), null, null, null, null, null, | |
+ ), | |
+ array( | |
+ '{"success": true, "error-codes": ["test"], "hostname": "google.com"}', | |
+ true, array(), 'google.com', null, null, null, null, | |
+ ), | |
+ array( | |
+ '{"success": false}', | |
+ false, array(ReCaptcha::E_UNKNOWN_ERROR), null, null, null, null, null, | |
+ ), | |
+ array( | |
+ '{"success": false, "hostname": "google.com"}', | |
+ false, array(ReCaptcha::E_UNKNOWN_ERROR), 'google.com', null, null, null, null, | |
+ ), | |
+ array( | |
+ 'BAD JSON', | |
+ false, array(ReCaptcha::E_INVALID_JSON), null, null, null, null, null, | |
+ ), | |
); | |
} | |
@@ -76,9 +113,53 @@ class ResponseTest extends \PHPUnit_Framework_TestCase | |
public function testGetHostname() | |
{ | |
- $hostname = 'google.com'; | |
- $errorCodes = array(); | |
- $response = new Response(true, $errorCodes, $hostname); | |
- $this->assertEquals($hostname, $response->getHostname()); | |
+ $hostname = 'google.com'; | |
+ $errorCodes = array(); | |
+ $response = new Response(true, $errorCodes, $hostname); | |
+ $this->assertEquals($hostname, $response->getHostname()); | |
+ } | |
+ | |
+ public function testGetChallengeTs() | |
+ { | |
+ $timestamp = 'timestamp'; | |
+ $errorCodes = array(); | |
+ $response = new Response(true, array(), 'hostname', $timestamp); | |
+ $this->assertEquals($timestamp, $response->getChallengeTs()); | |
+ } | |
+ | |
+ public function TestGetApkPackageName() | |
+ { | |
+ $apk = 'apk'; | |
+ $response = new Response(true, array(), 'hostname', 'timestamp', 'apk'); | |
+ $this->assertEquals($apk, $response->getApkPackageName()); | |
+ } | |
+ | |
+ public function testGetScore() | |
+ { | |
+ $score = 0.5; | |
+ $response = new Response(true, array(), 'hostname', 'timestamp', 'apk', $score); | |
+ $this->assertEquals($score, $response->getScore()); | |
+ } | |
+ | |
+ public function testGetAction() | |
+ { | |
+ $action = 'homepage'; | |
+ $response = new Response(true, array(), 'hostname', 'timestamp', 'apk', '0.5', 'homepage'); | |
+ $this->assertEquals($action, $response->getAction()); | |
+ } | |
+ | |
+ public function testToArray() | |
+ { | |
+ $response = new Response(true, array(), 'hostname', 'timestamp', 'apk', '0.5', 'homepage'); | |
+ $expected = array( | |
+ 'success' => true, | |
+ 'error-codes' => array(), | |
+ 'hostname' => 'hostname', | |
+ 'challenge_ts' => 'timestamp', | |
+ 'apk_package_name' => 'apk', | |
+ 'score' => 0.5, | |
+ 'action' => 'homepage', | |
+ ); | |
+ $this->assertEquals($expected, $response->toArray()); | |
} | |
} | |
diff --git a/recaptcha.libraries.yml b/recaptcha.libraries.yml | |
new file mode 100644 | |
index 0000000..7471b37 | |
--- /dev/null | |
+++ b/recaptcha.libraries.yml | |
@@ -0,0 +1,6 @@ | |
+recaptcha: | |
+ js: | |
+ js/recaptcha.js: {} | |
+ dependencies: | |
+ - core/drupal | |
+ - core/jquery | |
diff --git a/recaptcha.module b/recaptcha.module | |
index 674fa36..f821474 100644 | |
--- a/recaptcha.module | |
+++ b/recaptcha.module | |
@@ -71,6 +71,7 @@ function recaptcha_captcha($op, $captcha_type = '') { | |
$renderer = \Drupal::service('renderer'); | |
$recaptcha_site_key = $config->get('site_key'); | |
$recaptcha_secret_key = $config->get('secret_key'); | |
+ $recaptcha_use_globally = $config->get('use_globally'); | |
if (!empty($recaptcha_site_key) && !empty($recaptcha_secret_key)) { | |
// Build the reCAPTCHA captcha form if site_key and secret_key are | |
@@ -82,12 +83,25 @@ function recaptcha_captcha($op, $captcha_type = '') { | |
'#value' => 'Google no captcha', | |
]; | |
+ // As the validate callback does not depend on sid or solution, this | |
+ // captcha type can be displayed on cached pages. | |
+ $captcha['cacheable'] = TRUE; | |
+ | |
+ // Check if reCAPTCHA use globally is enabled. | |
+ $recaptcha_src = 'https://www.google.com/recaptcha/api.js'; | |
+ $recaptcha_src_fallback = 'https://www.google.com/recaptcha/api/fallback'; | |
+ if ($recaptcha_use_globally) { | |
+ $recaptcha_src = 'https://www.recaptcha.net/recaptcha/api.js'; | |
+ $recaptcha_src_fallback = 'https://www.recaptcha.net/recaptcha/api/fallback'; | |
+ } | |
+ | |
$noscript = ''; | |
if ($config->get('widget.noscript')) { | |
$recaptcha_widget_noscript = [ | |
'#theme' => 'recaptcha_widget_noscript', | |
'#widget' => [ | |
'sitekey' => $recaptcha_site_key, | |
+ 'recaptcha_src_fallback' => $recaptcha_src_fallback, | |
'language' => \Drupal::service('language_manager')->getCurrentLanguage()->getId(), | |
], | |
]; | |
@@ -102,6 +116,8 @@ function recaptcha_captcha($op, $captcha_type = '') { | |
'data-size' => $config->get('widget.size'), | |
'data-tabindex' => $config->get('widget.tabindex'), | |
]; | |
+ $captcha['form']['#attached']['library'][] = 'recaptcha/recaptcha'; | |
+ | |
// Filter out empty tabindex/size. | |
$attributes = array_filter($attributes); | |
@@ -114,7 +130,14 @@ function recaptcha_captcha($op, $captcha_type = '') { | |
[ | |
'#tag' => 'script', | |
'#attributes' => [ | |
- 'src' => Url::fromUri('https://www.google.com/recaptcha/api.js', ['query' => ['hl' => \Drupal::service('language_manager')->getCurrentLanguage()->getId()], 'absolute' => TRUE])->toString(), | |
+ 'src' => Url::fromUri($recaptcha_src, [ | |
+ 'query' => [ | |
+ 'hl' => \Drupal::service('language_manager')->getCurrentLanguage()->getId(), | |
+ 'onload' => 'drupalRecaptchaOnload', | |
+ 'render' => 'explicit', | |
+ ], | |
+ 'absolute' => TRUE, | |
+ ])->toString(), | |
'async' => TRUE, | |
'defer' => TRUE, | |
], | |
@@ -141,7 +164,9 @@ function recaptcha_captcha($op, $captcha_type = '') { | |
* CAPTCHA Callback; Validates the reCAPTCHA code. | |
*/ | |
function recaptcha_captcha_validation($solution, $response, $element, $form_state) { | |
- $recaptcha_secret_key = \Drupal::config('recaptcha.settings')->get('secret_key'); | |
+ $config = \Drupal::config('recaptcha.settings'); | |
+ | |
+ $recaptcha_secret_key = $config->get('secret_key'); | |
if (empty($_POST['g-recaptcha-response']) || empty($recaptcha_secret_key)) { | |
return FALSE; | |
} | |
@@ -149,6 +174,12 @@ function recaptcha_captcha_validation($solution, $response, $element, $form_stat | |
// Use Drupal::httpClient() to circumvent all issues with the Google library. | |
$recaptcha = new \ReCaptcha\ReCaptcha($recaptcha_secret_key, new \ReCaptcha\RequestMethod\Drupal8Post()); | |
+ // Ensures the hostname matches. Required if "Domain Name Validation" is | |
+ // disabled for credentials. | |
+ if ($config->get('verify_hostname')) { | |
+ $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME']); | |
+ } | |
+ | |
$resp = $recaptcha->verify( | |
$_POST['g-recaptcha-response'], | |
\Drupal::request()->getClientIp() | |
@@ -161,16 +192,24 @@ function recaptcha_captcha_validation($solution, $response, $element, $form_stat | |
else { | |
// Error code reference, https://developers.google.com/recaptcha/docs/verify | |
$error_codes = [ | |
- 'missing-input-secret' => t('The secret parameter is missing.'), | |
+ 'action-mismatch' => t('Expected action did not match.'), | |
+ 'apk_package_name-mismatch' => t('Expected APK package name did not match.'), | |
+ 'bad-response' => t('Did not receive a 200 from the service.'), | |
+ 'bad-request' => t('The request is invalid or malformed.'), | |
+ 'challenge-timeout' => t('Challenge timeout.'), | |
+ 'connection-failed' => t('Could not connect to service.'), | |
+ 'invalid-input-response' => t('The response parameter is invalid or malformed.'), | |
'invalid-input-secret' => t('The secret parameter is invalid or malformed.'), | |
'missing-input-response' => t('The response parameter is missing.'), | |
- 'invalid-input-response' => t('The response parameter is invalid or malformed.'), | |
+ 'missing-input-secret' => t('The secret parameter is missing.'), | |
+ 'hostname-mismatch' => t('Expected hostname did not match.'), | |
'invalid-json' => t('The json response is invalid or malformed.'), | |
- 'unknown' => t('Unknown error.'), | |
+ 'score-threshold-not-met' => t('Score threshold not met.'), | |
+ 'unknown-error' => t('Not a success, but no error codes received!'), | |
]; | |
foreach ($resp->getErrorCodes() as $code) { | |
if (!isset($error_codes[$code])) { | |
- $code = 'unknown'; | |
+ $code = 'unknown-error'; | |
} | |
\Drupal::logger('reCAPTCHA web service')->error('@error', ['@error' => $error_codes[$code]]); | |
} | |
@@ -184,7 +223,13 @@ function recaptcha_captcha_validation($solution, $response, $element, $form_stat | |
* @see recaptcha-widget-noscript.tpl.php | |
*/ | |
function template_preprocess_recaptcha_widget_noscript(&$variables) { | |
- $variables['sitekey'] = $variables['widget']['sitekey']; | |
+ $variables['sitekey'] = $variables['widget']['sitekey']; | |
$variables['language'] = $variables['widget']['language']; | |
- $variables['url'] = Url::fromUri('https://www.google.com/recaptcha/api/fallback', ['query' => ['k' => $variables['widget']['sitekey'], 'hl' => $variables['widget']['language']], 'absolute' => TRUE])->toString(); | |
+ $variables['url'] = Url::fromUri($variables['widget']['recaptcha_src_fallback'], [ | |
+ 'query' => [ | |
+ 'k' => $variables['widget']['sitekey'], | |
+ 'hl' => $variables['widget']['language'], | |
+ ], | |
+ 'absolute' => TRUE, | |
+ ])->toString(); | |
} | |
diff --git a/src/Form/ReCaptchaAdminSettingsForm.php b/src/Form/ReCaptchaAdminSettingsForm.php | |
index fd2ca6d..2029bf0 100644 | |
--- a/src/Form/ReCaptchaAdminSettingsForm.php | |
+++ b/src/Form/ReCaptchaAdminSettingsForm.php | |
@@ -39,7 +39,7 @@ class ReCaptchaAdminSettingsForm extends ConfigFormBase { | |
$form['general']['recaptcha_site_key'] = [ | |
'#default_value' => $config->get('site_key'), | |
- '#description' => $this->t('The site key given to you when you <a href=":url">register for reCAPTCHA</a>.', [':url' => 'http://www.google.com/recaptcha/admin']), | |
+ '#description' => $this->t('The site key given to you when you <a href=":url">register for reCAPTCHA</a>.', [':url' => 'https://www.google.com/recaptcha/admin']), | |
'#maxlength' => 40, | |
'#required' => TRUE, | |
'#title' => $this->t('Site key'), | |
@@ -48,13 +48,27 @@ class ReCaptchaAdminSettingsForm extends ConfigFormBase { | |
$form['general']['recaptcha_secret_key'] = [ | |
'#default_value' => $config->get('secret_key'), | |
- '#description' => $this->t('The secret key given to you when you <a href=":url">register for reCAPTCHA</a>.', [':url' => 'http://www.google.com/recaptcha/admin']), | |
+ '#description' => $this->t('The secret key given to you when you <a href=":url">register for reCAPTCHA</a>.', [':url' => 'https://www.google.com/recaptcha/admin']), | |
'#maxlength' => 40, | |
'#required' => TRUE, | |
'#title' => $this->t('Secret key'), | |
'#type' => 'textfield', | |
]; | |
+ $form['general']['recaptcha_verify_hostname'] = [ | |
+ '#default_value' => $config->get('verify_hostname'), | |
+ '#description' => $this->t('Checks the hostname on your server when verifying a solution. Enable this validation only, if <em>Verify the origin of reCAPTCHA solutions</em> is unchecked for your key pair. Provides crucial security by verifying requests come from one of your listed domains.'), | |
+ '#title' => $this->t('Local domain name validation'), | |
+ '#type' => 'checkbox', | |
+ ]; | |
+ | |
+ $form['general']['recaptcha_use_globally'] = [ | |
+ '#default_value' => $config->get('use_globally'), | |
+ '#description' => $this->t('Enable this in circumstances when "www.google.com" is not accessible, e.g. China.'), | |
+ '#title' => $this->t('Use reCAPTCHA globally'), | |
+ '#type' => 'checkbox', | |
+ ]; | |
+ | |
// Widget configurations. | |
$form['widget'] = [ | |
'#type' => 'details', | |
@@ -93,7 +107,7 @@ class ReCaptchaAdminSettingsForm extends ConfigFormBase { | |
]; | |
$form['widget']['recaptcha_tabindex'] = [ | |
'#default_value' => $config->get('widget.tabindex'), | |
- '#description' => $this->t('Set the <a href=":tabindex">tabindex</a> of the widget and challenge (Default = 0). If other elements in your page use tabindex, it should be set to make user navigation easier.', [':tabindex' => Url::fromUri('http://www.w3.org/TR/html4/interact/forms.html', ['fragment' => 'adef-tabindex'])->toString()]), | |
+ '#description' => $this->t('Set the <a href=":tabindex">tabindex</a> of the widget and challenge (Default = 0). If other elements in your page use tabindex, it should be set to make user navigation easier.', [':tabindex' => Url::fromUri('https://www.w3.org/TR/html4/interact/forms.html', ['fragment' => 'adef-tabindex'])->toString()]), | |
'#maxlength' => 4, | |
'#title' => $this->t('Tabindex'), | |
'#type' => 'number', | |
@@ -117,6 +131,8 @@ class ReCaptchaAdminSettingsForm extends ConfigFormBase { | |
$config | |
->set('site_key', $form_state->getValue('recaptcha_site_key')) | |
->set('secret_key', $form_state->getValue('recaptcha_secret_key')) | |
+ ->set('verify_hostname', $form_state->getValue('recaptcha_verify_hostname')) | |
+ ->set('use_globally', $form_state->getValue('recaptcha_use_globally')) | |
->set('widget.theme', $form_state->getValue('recaptcha_theme')) | |
->set('widget.type', $form_state->getValue('recaptcha_type')) | |
->set('widget.size', $form_state->getValue('recaptcha_size')) | |
diff --git a/src/ReCaptcha/RequestMethod/Drupal8Post.php b/src/ReCaptcha/RequestMethod/Drupal8Post.php | |
index 6080b3f..f840602 100644 | |
--- a/src/ReCaptcha/RequestMethod/Drupal8Post.php | |
+++ b/src/ReCaptcha/RequestMethod/Drupal8Post.php | |
@@ -2,7 +2,7 @@ | |
namespace ReCaptcha\RequestMethod; | |
-use GuzzleHttp\Exception\RequestException; | |
+use ReCaptcha\ReCaptcha; | |
use ReCaptcha\RequestMethod; | |
use ReCaptcha\RequestParameters; | |
@@ -11,13 +11,6 @@ use ReCaptcha\RequestParameters; | |
*/ | |
class Drupal8Post implements RequestMethod { | |
- /** | |
- * URL to which requests are POSTed. | |
- * | |
- * @const string | |
- */ | |
- const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify'; | |
- | |
/** | |
* Submit the POST request with the specified parameters. | |
* | |
@@ -29,22 +22,30 @@ class Drupal8Post implements RequestMethod { | |
*/ | |
public function submit(RequestParameters $params) { | |
- try { | |
- $options = [ | |
- 'headers' => [ | |
- 'Content-type' => 'application/x-www-form-urlencoded', | |
- ], | |
- 'body' => $params->toQueryString(), | |
- ]; | |
- | |
- $response = \Drupal::httpClient()->post(self::SITE_VERIFY_URL, $options); | |
+ $options = [ | |
+ 'headers' => [ | |
+ 'Content-type' => 'application/x-www-form-urlencoded', | |
+ ], | |
+ 'body' => $params->toQueryString(), | |
+ // Stop firing exception on response status code >= 300. | |
+ // See http://docs.guzzlephp.org/en/stable/handlers-and-middleware.html | |
+ 'http_errors' => FALSE, | |
+ ]; | |
+ | |
+ $response = \Drupal::httpClient()->post(ReCaptcha::SITE_VERIFY_URL, $options); | |
+ | |
+ if ($response->getStatusCode() == 200) { | |
+ // The service request was successful. | |
+ return (string) $response->getBody(); | |
} | |
- catch (RequestException $exception) { | |
- \Drupal::logger('reCAPTCHA web service')->error($exception); | |
- return ''; | |
+ elseif ($response->getStatusCode() < 0) { | |
+ // Negative status codes typically point to network or socket issues. | |
+ return '{"success": false, "error-codes": ["' . ReCaptcha::E_CONNECTION_FAILED . '"]}'; | |
+ } | |
+ else { | |
+ // Positive none 200 status code typically means the request has failed. | |
+ return '{"success": false, "error-codes": ["' . ReCaptcha::E_BAD_RESPONSE . '"]}'; | |
} | |
- | |
- return (string) $response->getBody(); | |
} | |
} | |
diff --git a/src/Tests/ReCaptchaBasicTest.php b/src/Tests/ReCaptchaBasicTest.php | |
deleted file mode 100644 | |
index 9d9ca8f..0000000 | |
--- a/src/Tests/ReCaptchaBasicTest.php | |
+++ /dev/null | |
@@ -1,178 +0,0 @@ | |
-<?php | |
- | |
-namespace Drupal\recaptcha\Tests; | |
- | |
-use Drupal\simpletest\WebTestBase; | |
- | |
-/** | |
- * Test basic functionality of reCAPTCHA module. | |
- * | |
- * @group reCAPTCHA | |
- * | |
- * @dependencies captcha | |
- */ | |
-class ReCaptchaBasicTest extends WebTestBase { | |
- | |
- /** | |
- * Modules to enable. | |
- * | |
- * @var array | |
- */ | |
- public static $modules = ['recaptcha', 'captcha']; | |
- | |
- /** | |
- * {@inheritdoc} | |
- */ | |
- protected function setUp() { | |
- parent::setUp(); | |
- module_load_include('inc', 'captcha'); | |
- | |
- // Create a normal user. | |
- $permissions = [ | |
- 'access content', | |
- ]; | |
- $this->normal_user = $this->drupalCreateUser($permissions); | |
- | |
- // Create an admin user. | |
- $permissions += [ | |
- 'administer CAPTCHA settings', | |
- 'skip CAPTCHA', | |
- 'administer permissions', | |
- 'administer content types', | |
- 'administer recaptcha', | |
- ]; | |
- $this->admin_user = $this->drupalCreateUser($permissions); | |
- } | |
- | |
- /** | |
- * Test access to the administration page. | |
- */ | |
- public function testReCaptchaAdminAccess() { | |
- $this->drupalLogin($this->admin_user); | |
- $this->drupalGet('admin/config/people/captcha/recaptcha'); | |
- $this->assertNoText(t('Access denied'), 'Admin users should be able to access the reCAPTCHA admin page', 'reCAPTCHA'); | |
- $this->drupalLogout(); | |
- } | |
- | |
- /** | |
- * Test the reCAPTCHA settings form. | |
- */ | |
- public function testReCaptchaAdminSettingsForm() { | |
- $this->drupalLogin($this->admin_user); | |
- | |
- $site_key = $this->randomMachineName(40); | |
- $secret_key = $this->randomMachineName(40); | |
- | |
- // Check form validation. | |
- $edit['recaptcha_site_key'] = ''; | |
- $edit['recaptcha_secret_key'] = ''; | |
- $this->drupalPostForm('admin/config/people/captcha/recaptcha', $edit, t('Save configuration')); | |
- | |
- $this->assertRaw(t('Site key field is required.'), '[testReCaptchaConfiguration]: Empty site key detected.'); | |
- $this->assertRaw(t('Secret key field is required.'), '[testReCaptchaConfiguration]: Empty secret key detected.'); | |
- | |
- // Save form with valid values. | |
- $edit['recaptcha_site_key'] = $site_key; | |
- $edit['recaptcha_secret_key'] = $secret_key; | |
- $edit['recaptcha_tabindex'] = 0; | |
- $this->drupalPostForm('admin/config/people/captcha/recaptcha', $edit, t('Save configuration')); | |
- $this->assertRaw(t('The configuration options have been saved.'), '[testReCaptchaConfiguration]: The configuration options have been saved.'); | |
- | |
- $this->assertNoRaw(t('Site key field is required.'), '[testReCaptchaConfiguration]: Site key was not empty.'); | |
- $this->assertNoRaw(t('Secret key field is required.'), '[testReCaptchaConfiguration]: Secret key was not empty.'); | |
- $this->assertNoRaw(t('The tabindex must be an integer.'), '[testReCaptchaConfiguration]: Tab index had a valid input.'); | |
- | |
- $this->drupalLogout(); | |
- } | |
- | |
- /** | |
- * Testing the protection of the user login form. | |
- */ | |
- public function testReCaptchaOnLoginForm() { | |
- $site_key = $this->randomMachineName(40); | |
- $secret_key = $this->randomMachineName(40); | |
- $grecaptcha = '<div class="g-recaptcha" data-sitekey="' . $site_key . '" data-theme="light" data-type="image"></div>'; | |
- | |
- // Test if login works. | |
- $this->drupalLogin($this->normal_user); | |
- $this->drupalLogout(); | |
- | |
- $this->drupalGet('user/login'); | |
- $this->assertNoRaw($grecaptcha, '[testReCaptchaOnLoginForm]: reCAPTCHA is not shown on form.'); | |
- | |
- // Enable 'captcha/Math' CAPTCHA on login form. | |
- captcha_set_form_id_setting('user_login_form', 'captcha/Math'); | |
- | |
- $this->drupalGet('user/login'); | |
- $this->assertNoRaw($grecaptcha, '[testReCaptchaOnLoginForm]: reCAPTCHA is not shown on form.'); | |
- | |
- // Enable 'recaptcha/reCAPTCHA' on login form. | |
- captcha_set_form_id_setting('user_login_form', 'recaptcha/reCAPTCHA'); | |
- $result = captcha_get_form_id_setting('user_login_form'); | |
- $this->assertNotNull($result, 'A configuration has been found for CAPTCHA point: user_login_form', 'reCAPTCHA'); | |
- $this->assertEqual($result->getCaptchaType(), 'recaptcha/reCAPTCHA', 'reCAPTCHA type has been configured for CAPTCHA point: user_login_form', 'reCAPTCHA'); | |
- | |
- // Check if a Math CAPTCHA is still shown on the login form. The site key | |
- // and security key have not yet configured for reCAPTCHA. The module need | |
- // to fall back to math captcha. | |
- $this->drupalGet('user/login'); | |
- $this->assertRaw(t('Math question'), '[testReCaptchaOnLoginForm]: Math CAPTCHA is shown on form.'); | |
- | |
- // Configure site key and security key to show reCAPTCHA and no fall back. | |
- $this->config('recaptcha.settings')->set('site_key', $site_key)->save(); | |
- $this->config('recaptcha.settings')->set('secret_key', $secret_key)->save(); | |
- | |
- // Check if there is a reCAPTCHA on the login form. | |
- $this->drupalGet('user/login'); | |
- $this->assertRaw($grecaptcha, '[testReCaptchaOnLoginForm]: reCAPTCHA is shown on form.'); | |
- $this->assertRaw('<script src="https://www.google.com/recaptcha/api.js?hl=' . \Drupal::service('language_manager')->getCurrentLanguage()->getId() . '" async defer></script>', '[testReCaptchaOnLoginForm]: reCAPTCHA is shown on form.'); | |
- $this->assertNoRaw($grecaptcha . '<noscript>', '[testReCaptchaOnLoginForm]: NoScript code is not enabled for the reCAPTCHA.'); | |
- | |
- // Test if the fall back url is properly build and noscript code added. | |
- $this->config('recaptcha.settings')->set('widget.noscript', 1)->save(); | |
- | |
- $this->drupalGet('user/login'); | |
- $this->assertRaw($grecaptcha . "\n" . '<noscript>', '[testReCaptchaOnLoginForm]: NoScript for reCAPTCHA is shown on form.'); | |
- $this->assertRaw('https://www.google.com/recaptcha/api/fallback?k=' . $site_key . '&hl=' . \Drupal::service('language_manager')->getCurrentLanguage()->getId(), '[testReCaptchaOnLoginForm]: Fallback URL with IFRAME has been found.'); | |
- | |
- // Check that data-size attribute does not exists. | |
- $this->config('recaptcha.settings')->set('widget.size', '')->save(); | |
- $this->drupalGet('user/login'); | |
- $element = $this->xpath('//div[@class=:class and @data-size=:size]', [':class' => 'g-recaptcha', ':size' => 'small']); | |
- $this->assertFalse(!empty($element), 'Tag contains no data-size attribute.'); | |
- | |
- // Check that data-size attribute exists. | |
- $this->config('recaptcha.settings')->set('widget.size', 'small')->save(); | |
- $this->drupalGet('user/login'); | |
- $element = $this->xpath('//div[@class=:class and @data-size=:size]', [':class' => 'g-recaptcha', ':size' => 'small']); | |
- $this->assertTrue(!empty($element), 'Tag contains data-size attribute and value.'); | |
- | |
- // Check that data-tabindex attribute does not exists. | |
- $this->config('recaptcha.settings')->set('widget.tabindex', 0)->save(); | |
- $this->drupalGet('user/login'); | |
- $element = $this->xpath('//div[@class=:class and @data-tabindex=:index]', [':class' => 'g-recaptcha', ':index' => 0]); | |
- $this->assertFalse(!empty($element), 'Tag contains no data-tabindex attribute.'); | |
- | |
- // Check that data-tabindex attribute exists. | |
- $this->config('recaptcha.settings')->set('widget.tabindex', 5)->save(); | |
- $this->drupalGet('user/login'); | |
- $element = $this->xpath('//div[@class=:class and @data-tabindex=:index]', [':class' => 'g-recaptcha', ':index' => 5]); | |
- $this->assertTrue(!empty($element), 'Tag contains data-tabindex attribute and value.'); | |
- | |
- // Try to log in, which should fail. | |
- $edit['name'] = $this->normal_user->getUsername(); | |
- $edit['pass'] = $this->normal_user->getPassword(); | |
- $edit['captcha_response'] = '?'; | |
- | |
- $this->drupalPostForm('user/login', $edit, t('Log in')); | |
- // Check for error message. | |
- $this->assertText(t('The answer you entered for the CAPTCHA was not correct.'), 'CAPTCHA should block user login form', 'reCAPTCHA'); | |
- | |
- // And make sure that user is not logged in: check for name and password | |
- // fields on "?q=user". | |
- $this->drupalGet('user/login'); | |
- $this->assertField('name', t('Username field found.'), 'reCAPTCHA'); | |
- $this->assertField('pass', t('Password field found.'), 'reCAPTCHA'); | |
- } | |
- | |
-} | |
diff --git a/tests/src/Functional/ReCaptchaBasicTest.php b/tests/src/Functional/ReCaptchaBasicTest.php | |
new file mode 100644 | |
index 0000000..eb40641 | |
--- /dev/null | |
+++ b/tests/src/Functional/ReCaptchaBasicTest.php | |
@@ -0,0 +1,233 @@ | |
+<?php | |
+ | |
+namespace Drupal\Tests\recaptcha\Functional; | |
+ | |
+use Drupal\Core\Url; | |
+use Drupal\Component\Utility\Html; | |
+use Drupal\Tests\BrowserTestBase; | |
+ | |
+/** | |
+ * Test basic functionality of reCAPTCHA module. | |
+ * | |
+ * @group reCAPTCHA | |
+ * | |
+ * @dependencies captcha | |
+ */ | |
+class ReCaptchaBasicTest extends BrowserTestBase { | |
+ | |
+ /** | |
+ * A normal user. | |
+ * | |
+ * @var \Drupal\user\UserInterface | |
+ */ | |
+ protected $normalUser; | |
+ | |
+ /** | |
+ * An admin user. | |
+ * | |
+ * @var \Drupal\user\UserInterface | |
+ */ | |
+ protected $adminUser; | |
+ | |
+ /** | |
+ * Modules to enable. | |
+ * | |
+ * @var array | |
+ */ | |
+ public static $modules = ['recaptcha', 'captcha']; | |
+ | |
+ /** | |
+ * {@inheritdoc} | |
+ */ | |
+ protected function setUp() { | |
+ parent::setUp(); | |
+ module_load_include('inc', 'captcha'); | |
+ | |
+ // Create a normal user. | |
+ $permissions = [ | |
+ 'access content', | |
+ ]; | |
+ $this->normalUser = $this->drupalCreateUser($permissions); | |
+ | |
+ // Create an admin user. | |
+ $permissions += [ | |
+ 'administer CAPTCHA settings', | |
+ 'skip CAPTCHA', | |
+ 'administer permissions', | |
+ 'administer content types', | |
+ 'administer recaptcha', | |
+ ]; | |
+ $this->adminUser = $this->drupalCreateUser($permissions); | |
+ } | |
+ | |
+ /** | |
+ * Test access to the administration page. | |
+ */ | |
+ public function testReCaptchaAdminAccess() { | |
+ $this->drupalLogin($this->adminUser); | |
+ $this->drupalGet('admin/config/people/captcha/recaptcha'); | |
+ $this->assertSession()->pageTextNotContains(t('Access denied'), 'Admin users should be able to access the reCAPTCHA admin page', 'reCAPTCHA'); | |
+ $this->drupalLogout(); | |
+ } | |
+ | |
+ /** | |
+ * Test the reCAPTCHA settings form. | |
+ */ | |
+ public function testReCaptchaAdminSettingsForm() { | |
+ $this->drupalLogin($this->adminUser); | |
+ | |
+ $site_key = $this->randomMachineName(40); | |
+ $secret_key = $this->randomMachineName(40); | |
+ | |
+ // Check form validation. | |
+ $edit['recaptcha_site_key'] = ''; | |
+ $edit['recaptcha_secret_key'] = ''; | |
+ $this->drupalPostForm('admin/config/people/captcha/recaptcha', $edit, t('Save configuration')); | |
+ | |
+ $this->assertSession()->responseContains(t('Site key field is required.'), '[testReCaptchaConfiguration]: Empty site key detected.'); | |
+ $this->assertSession()->responseContains(t('Secret key field is required.'), '[testReCaptchaConfiguration]: Empty secret key detected.'); | |
+ | |
+ // Save form with valid values. | |
+ $edit['recaptcha_site_key'] = $site_key; | |
+ $edit['recaptcha_secret_key'] = $secret_key; | |
+ $edit['recaptcha_tabindex'] = 0; | |
+ $this->drupalPostForm('admin/config/people/captcha/recaptcha', $edit, t('Save configuration')); | |
+ $this->assertSession()->responseContains(t('The configuration options have been saved.'), '[testReCaptchaConfiguration]: The configuration options have been saved.'); | |
+ | |
+ $this->assertSession()->responseNotContains(t('Site key field is required.'), '[testReCaptchaConfiguration]: Site key was not empty.'); | |
+ $this->assertSession()->responseNotContains(t('Secret key field is required.'), '[testReCaptchaConfiguration]: Secret key was not empty.'); | |
+ $this->assertSession()->responseNotContains(t('The tabindex must be an integer.'), '[testReCaptchaConfiguration]: Tab index had a valid input.'); | |
+ | |
+ $this->drupalLogout(); | |
+ } | |
+ | |
+ /** | |
+ * Testing the protection of the user login form. | |
+ */ | |
+ public function testReCaptchaOnLoginForm() { | |
+ $site_key = $this->randomMachineName(40); | |
+ $secret_key = $this->randomMachineName(40); | |
+ $grecaptcha = '<div class="g-recaptcha" data-sitekey="' . $site_key . '" data-theme="light" data-type="image"></div>'; | |
+ | |
+ // Test if login works. | |
+ $this->drupalLogin($this->normalUser); | |
+ $this->drupalLogout(); | |
+ | |
+ $this->drupalGet('user/login'); | |
+ $this->assertSession()->responseNotContains($grecaptcha, '[testReCaptchaOnLoginForm]: reCAPTCHA is not shown on form.'); | |
+ | |
+ // Enable 'captcha/Math' CAPTCHA on login form. | |
+ captcha_set_form_id_setting('user_login_form', 'captcha/Math'); | |
+ | |
+ $this->drupalGet('user/login'); | |
+ $this->assertSession()->responseNotContains($grecaptcha, '[testReCaptchaOnLoginForm]: reCAPTCHA is not shown on form.'); | |
+ | |
+ // Enable 'recaptcha/reCAPTCHA' on login form. | |
+ captcha_set_form_id_setting('user_login_form', 'recaptcha/reCAPTCHA'); | |
+ $result = captcha_get_form_id_setting('user_login_form'); | |
+ $this->assertNotNull($result, 'A configuration has been found for CAPTCHA point: user_login_form', 'reCAPTCHA'); | |
+ $this->assertEquals($result->getCaptchaType(), 'recaptcha/reCAPTCHA', 'reCAPTCHA type has been configured for CAPTCHA point: user_login_form'); | |
+ | |
+ // Check if a Math CAPTCHA is still shown on the login form. The site key | |
+ // and security key have not yet configured for reCAPTCHA. The module need | |
+ // to fall back to math captcha. | |
+ $this->drupalGet('user/login'); | |
+ $this->assertSession()->responseContains(t('Math question'), '[testReCaptchaOnLoginForm]: Math CAPTCHA is shown on form.'); | |
+ | |
+ // Configure site key and security key to show reCAPTCHA and no fall back. | |
+ $this->config('recaptcha.settings')->set('site_key', $site_key)->save(); | |
+ $this->config('recaptcha.settings')->set('secret_key', $secret_key)->save(); | |
+ | |
+ // Check if there is a reCAPTCHA on the login form. | |
+ $this->drupalGet('user/login'); | |
+ $this->assertSession()->responseContains($grecaptcha, '[testReCaptchaOnLoginForm]: reCAPTCHA is shown on form.'); | |
+ $options = [ | |
+ 'query' => [ | |
+ 'hl' => \Drupal::service('language_manager')->getCurrentLanguage()->getId(), | |
+ 'onload' => 'drupalRecaptchaOnload', | |
+ 'render' => 'explicit', | |
+ ], | |
+ 'absolute' => TRUE, | |
+ ]; | |
+ $this->assertSession()->responseContains(Html::escape(Url::fromUri('https://www.google.com/recaptcha/api.js', $options)->toString()), '[testReCaptchaOnLoginForm]: reCAPTCHA is shown on form.'); | |
+ $this->assertSession()->responseNotContains($grecaptcha . '<noscript>', '[testReCaptchaOnLoginForm]: NoScript code is not enabled for the reCAPTCHA.'); | |
+ | |
+ // Test if the fall back url is properly build and noscript code added. | |
+ $this->config('recaptcha.settings')->set('widget.noscript', 1)->save(); | |
+ | |
+ $this->drupalGet('user/login'); | |
+ $this->assertSession()->responseContains($grecaptcha . "\n" . '<noscript>', '[testReCaptchaOnLoginForm]: NoScript for reCAPTCHA is shown on form.'); | |
+ $options = [ | |
+ 'query' => [ | |
+ 'k' => $site_key, | |
+ 'hl' => \Drupal::service('language_manager')->getCurrentLanguage()->getId(), | |
+ ], | |
+ 'absolute' => TRUE, | |
+ ]; | |
+ $this->assertSession()->responseContains(Html::escape(Url::fromUri('https://www.google.com/recaptcha/api/fallback', $options)->toString()), '[testReCaptchaOnLoginForm]: Fallback URL with IFRAME has been found.'); | |
+ | |
+ // Check if there is a reCAPTCHA with global url on the login form. | |
+ $this->config('recaptcha.settings')->set('use_globally', TRUE)->save(); | |
+ $this->drupalGet('user/login'); | |
+ $options = [ | |
+ 'query' => [ | |
+ 'hl' => \Drupal::service('language_manager')->getCurrentLanguage()->getId(), | |
+ 'onload' => 'drupalRecaptchaOnload', | |
+ 'render' => 'explicit', | |
+ ], | |
+ 'absolute' => TRUE, | |
+ ]; | |
+ $this->assertSession()->responseContains(Html::escape(Url::fromUri('https://www.recaptcha.net/recaptcha/api.js', $options)->toString()), '[testReCaptchaOnLoginForm]: Global reCAPTCHA is shown on form.'); | |
+ $options = [ | |
+ 'query' => [ | |
+ 'k' => $site_key, | |
+ 'hl' => \Drupal::service('language_manager')->getCurrentLanguage()->getId(), | |
+ ], | |
+ 'absolute' => TRUE, | |
+ ]; | |
+ $this->assertSession()->responseContains(Html::escape(Url::fromUri('https://www.recaptcha.net/recaptcha/api/fallback', $options)->toString()), '[testReCaptchaOnLoginForm]: Global fallback URL with IFRAME has been found.'); | |
+ | |
+ // Check that data-size attribute does not exists. | |
+ $this->config('recaptcha.settings')->set('widget.size', '')->save(); | |
+ $this->drupalGet('user/login'); | |
+ $element = $this->xpath('//div[@class=:class and @data-size=:size]', [':class' => 'g-recaptcha', ':size' => 'small']); | |
+ $this->assertFalse(!empty($element), 'Tag contains no data-size attribute.'); | |
+ | |
+ // Check that data-size attribute exists. | |
+ $this->config('recaptcha.settings')->set('widget.size', 'small')->save(); | |
+ $this->drupalGet('user/login'); | |
+ $element = $this->xpath('//div[@class=:class and @data-size=:size]', [':class' => 'g-recaptcha', ':size' => 'small']); | |
+ $this->assertTrue(!empty($element), 'Tag contains data-size attribute and value.'); | |
+ | |
+ // Check that data-tabindex attribute does not exists. | |
+ $this->config('recaptcha.settings')->set('widget.tabindex', 0)->save(); | |
+ $this->drupalGet('user/login'); | |
+ $element = $this->xpath('//div[@class=:class and @data-tabindex=:index]', [':class' => 'g-recaptcha', ':index' => 0]); | |
+ $this->assertFalse(!empty($element), 'Tag contains no data-tabindex attribute.'); | |
+ | |
+ // Check that data-tabindex attribute exists. | |
+ $this->config('recaptcha.settings')->set('widget.tabindex', 5)->save(); | |
+ $this->drupalGet('user/login'); | |
+ $element = $this->xpath('//div[@class=:class and @data-tabindex=:index]', [':class' => 'g-recaptcha', ':index' => 5]); | |
+ $this->assertTrue(!empty($element), 'Tag contains data-tabindex attribute and value.'); | |
+ | |
+ // Try to log in, which should fail. | |
+ $edit['name'] = $this->normalUser->getAccountName(); | |
+ $edit['pass'] = $this->normalUser->getPassword(); | |
+ $this->assertSession()->responseContains('captcha_response'); | |
+ $this->assertSession() | |
+ ->hiddenFieldExists('captcha_response') | |
+ ->setValue('?'); | |
+ | |
+ $this->drupalPostForm('user/login', $edit, t('Log in')); | |
+ // Check for error message. | |
+ $this->assertSession()->pageTextContains(t('The answer you entered for the CAPTCHA was not correct.'), 'CAPTCHA should block user login form', 'reCAPTCHA'); | |
+ | |
+ // And make sure that user is not logged in: check for name and password | |
+ // fields on "?q=user". | |
+ $this->drupalGet('user/login'); | |
+ $this->assertSession()->fieldExists('name'); | |
+ $this->assertSession()->fieldExists('pass'); | |
+ } | |
+ | |
+} | |
-- | |
2.19.1 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment