diff --git a/composer.json b/composer.json index c704b03b05db8764865bb799103c46cb62ffab67..97457c5e3427b4e02f34ced203c17fb584dc6370 100644 --- a/composer.json +++ b/composer.json @@ -153,6 +153,7 @@ "drupal/realname": "1.0.0-rc2", "drupal/rebuild_cache_access": "1.7", "drupal/recaptcha": "2.5", + "drupal/recaptcha_v3": "^1.3", "drupal/redirect": "1.6", "drupal/redis": "1.0", "drupal/roleassign": "1.0.0-beta1", diff --git a/composer.lock b/composer.lock index ce448e5b59972b64313ae918a586b48dddae5899..cd8d811b39210f8612ab480f2044fff63a5837b5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d6554279c8afdbfe4d362bbc49c473bd", + "content-hash": "94120cd37bec1a986ba663ef2d31f53b", "packages": [ { "name": "alchemy/zippy", @@ -6801,6 +6801,60 @@ "issues": "https://www.drupal.org/project/issues/recaptcha" } }, + { + "name": "drupal/recaptcha_v3", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/recaptcha_v3.git", + "reference": "8.x-1.3" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/recaptcha_v3-8.x-1.3.zip", + "reference": "8.x-1.3", + "shasum": "1a228dafb57317889c18bbc80eafa40696673125" + }, + "require": { + "drupal/captcha": "^1.0.0-beta4", + "drupal/core": "^8 || ^9", + "google/recaptcha": "^1.2" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.3", + "datestamp": "1590393627", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "B-Prod", + "homepage": "https://www.drupal.org/user/407852" + }, + { + "name": "dench0", + "homepage": "https://www.drupal.org/user/896504" + }, + { + "name": "majid.ali", + "homepage": "https://www.drupal.org/user/1271330" + } + ], + "description": "The reCaptcha V3 module provides integration with Google reCaptcha V3 and CAPTCHA module.", + "homepage": "https://www.drupal.org/project/recaptcha_v3", + "support": { + "source": "https://git.drupalcode.org/project/recaptcha_v3" + } + }, { "name": "drupal/redirect", "version": "1.6.0", @@ -9143,6 +9197,53 @@ ], "time": "2019-11-13T10:30:21+00:00" }, + { + "name": "google/recaptcha", + "version": "1.2.4", + "source": { + "type": "git", + "url": "https://github.com/google/recaptcha.git", + "reference": "614f25a9038be4f3f2da7cbfd778dc5b357d2419" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/google/recaptcha/zipball/614f25a9038be4f3f2da7cbfd778dc5b357d2419", + "reference": "614f25a9038be4f3f2da7cbfd778dc5b357d2419", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.2.20|^2.15", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^4.8.36|^5.7.27|^6.59|^7.5.11" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "ReCaptcha\\": "src/ReCaptcha" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Client library for reCAPTCHA, a free service that protects websites from spam and abuse.", + "homepage": "https://www.google.com/recaptcha/", + "keywords": [ + "Abuse", + "captcha", + "recaptcha", + "spam" + ], + "time": "2020-03-31T17:50:54+00:00" + }, { "name": "grasmash/expander", "version": "1.0.0", diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 31c343ffb6fb749407471baac5c63f9872e9a170..d22d230810fd26ece7dd7a94fe61e832b601bf0e 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -3908,6 +3908,15 @@ 'Psy\\VersionUpdater\\GitHubChecker' => $vendorDir . '/psy/psysh/src/VersionUpdater/GitHubChecker.php', 'Psy\\VersionUpdater\\IntervalChecker' => $vendorDir . '/psy/psysh/src/VersionUpdater/IntervalChecker.php', 'Psy\\VersionUpdater\\NoopChecker' => $vendorDir . '/psy/psysh/src/VersionUpdater/NoopChecker.php', + 'ReCaptcha\\ReCaptcha' => $vendorDir . '/google/recaptcha/src/ReCaptcha/ReCaptcha.php', + 'ReCaptcha\\RequestMethod' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod.php', + 'ReCaptcha\\RequestMethod\\Curl' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php', + 'ReCaptcha\\RequestMethod\\CurlPost' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php', + 'ReCaptcha\\RequestMethod\\Post' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php', + 'ReCaptcha\\RequestMethod\\Socket' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php', + 'ReCaptcha\\RequestMethod\\SocketPost' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php', + 'ReCaptcha\\RequestParameters' => $vendorDir . '/google/recaptcha/src/ReCaptcha/RequestParameters.php', + 'ReCaptcha\\Response' => $vendorDir . '/google/recaptcha/src/ReCaptcha/Response.php', 'RobRichards\\XMLSecLibs\\Utils\\XPath' => $vendorDir . '/robrichards/xmlseclibs/src/Utils/XPath.php', 'RobRichards\\XMLSecLibs\\XMLSecEnc' => $vendorDir . '/robrichards/xmlseclibs/src/XMLSecEnc.php', 'RobRichards\\XMLSecLibs\\XMLSecurityDSig' => $vendorDir . '/robrichards/xmlseclibs/src/XMLSecurityDSig.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index d743c08493304e8521937046f78a616705af9fa7..a7052bede15d65a365fec96594c12e0564cc5e87 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -51,6 +51,7 @@ 'SelfUpdate\\' => array($vendorDir . '/consolidation/self-update/src'), 'Robo\\' => array($vendorDir . '/consolidation/robo/src'), 'RobRichards\\XMLSecLibs\\' => array($vendorDir . '/robrichards/xmlseclibs/src'), + 'ReCaptcha\\' => array($vendorDir . '/google/recaptcha/src/ReCaptcha'), 'Psy\\' => array($vendorDir . '/psy/psysh/src'), 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 81a723b4fd07b3332260da91d65f880bace2742d..77914a8ffa6da6eb254577a537626f9b979c5342 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -115,6 +115,7 @@ class ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530 array ( 'Robo\\' => 5, 'RobRichards\\XMLSecLibs\\' => 23, + 'ReCaptcha\\' => 10, ), 'P' => array ( @@ -387,6 +388,10 @@ class ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530 array ( 0 => __DIR__ . '/..' . '/robrichards/xmlseclibs/src', ), + 'ReCaptcha\\' => + array ( + 0 => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha', + ), 'Psy\\' => array ( 0 => __DIR__ . '/..' . '/psy/psysh/src', @@ -4585,6 +4590,15 @@ class ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530 'Psy\\VersionUpdater\\GitHubChecker' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/GitHubChecker.php', 'Psy\\VersionUpdater\\IntervalChecker' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/IntervalChecker.php', 'Psy\\VersionUpdater\\NoopChecker' => __DIR__ . '/..' . '/psy/psysh/src/VersionUpdater/NoopChecker.php', + 'ReCaptcha\\ReCaptcha' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/ReCaptcha.php', + 'ReCaptcha\\RequestMethod' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod.php', + 'ReCaptcha\\RequestMethod\\Curl' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php', + 'ReCaptcha\\RequestMethod\\CurlPost' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php', + 'ReCaptcha\\RequestMethod\\Post' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php', + 'ReCaptcha\\RequestMethod\\Socket' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php', + 'ReCaptcha\\RequestMethod\\SocketPost' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php', + 'ReCaptcha\\RequestParameters' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/RequestParameters.php', + 'ReCaptcha\\Response' => __DIR__ . '/..' . '/google/recaptcha/src/ReCaptcha/Response.php', 'RobRichards\\XMLSecLibs\\Utils\\XPath' => __DIR__ . '/..' . '/robrichards/xmlseclibs/src/Utils/XPath.php', 'RobRichards\\XMLSecLibs\\XMLSecEnc' => __DIR__ . '/..' . '/robrichards/xmlseclibs/src/XMLSecEnc.php', 'RobRichards\\XMLSecLibs\\XMLSecurityDSig' => __DIR__ . '/..' . '/robrichards/xmlseclibs/src/XMLSecurityDSig.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index f85fa27716f08ac73a089a8394d67ad32486fab5..21228e8a2aaf7ec3d6c21f3838ea854e7764e73e 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -7009,6 +7009,62 @@ "issues": "https://www.drupal.org/project/issues/recaptcha" } }, + { + "name": "drupal/recaptcha_v3", + "version": "1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://git.drupalcode.org/project/recaptcha_v3.git", + "reference": "8.x-1.3" + }, + "dist": { + "type": "zip", + "url": "https://ftp.drupal.org/files/projects/recaptcha_v3-8.x-1.3.zip", + "reference": "8.x-1.3", + "shasum": "1a228dafb57317889c18bbc80eafa40696673125" + }, + "require": { + "drupal/captcha": "^1.0.0-beta4", + "drupal/core": "^8 || ^9", + "google/recaptcha": "^1.2" + }, + "type": "drupal-module", + "extra": { + "drupal": { + "version": "8.x-1.3", + "datestamp": "1590393627", + "security-coverage": { + "status": "covered", + "message": "Covered by Drupal's security advisory policy" + } + } + }, + "installation-source": "dist", + "notification-url": "https://packages.drupal.org/8/downloads", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "B-Prod", + "homepage": "https://www.drupal.org/user/407852" + }, + { + "name": "dench0", + "homepage": "https://www.drupal.org/user/896504" + }, + { + "name": "majid.ali", + "homepage": "https://www.drupal.org/user/1271330" + } + ], + "description": "The reCaptcha V3 module provides integration with Google reCaptcha V3 and CAPTCHA module.", + "homepage": "https://www.drupal.org/project/recaptcha_v3", + "support": { + "source": "https://git.drupalcode.org/project/recaptcha_v3" + } + }, { "name": "drupal/redirect", "version": "1.6.0", @@ -9430,6 +9486,55 @@ "unicode" ] }, + { + "name": "google/recaptcha", + "version": "1.2.4", + "version_normalized": "1.2.4.0", + "source": { + "type": "git", + "url": "https://github.com/google/recaptcha.git", + "reference": "614f25a9038be4f3f2da7cbfd778dc5b357d2419" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/google/recaptcha/zipball/614f25a9038be4f3f2da7cbfd778dc5b357d2419", + "reference": "614f25a9038be4f3f2da7cbfd778dc5b357d2419", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.2.20|^2.15", + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^4.8.36|^5.7.27|^6.59|^7.5.11" + }, + "time": "2020-03-31T17:50:54+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "ReCaptcha\\": "src/ReCaptcha" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Client library for reCAPTCHA, a free service that protects websites from spam and abuse.", + "homepage": "https://www.google.com/recaptcha/", + "keywords": [ + "Abuse", + "captcha", + "recaptcha", + "spam" + ] + }, { "name": "grasmash/expander", "version": "1.0.0", diff --git a/vendor/google/recaptcha/.github/ISSUE_TEMPLATE/bug_report.md b/vendor/google/recaptcha/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000000000000000000000000000000..a14dcfeebb8ceaa0c9d7a382a351644ea87a9140 --- /dev/null +++ b/vendor/google/recaptcha/.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/vendor/google/recaptcha/.gitignore b/vendor/google/recaptcha/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..820a8c6d441b999e36a3959c1cf47e5515921814 --- /dev/null +++ b/vendor/google/recaptcha/.gitignore @@ -0,0 +1,7 @@ +/.php_cs.cache +/.phpunit.result.cache +/build +/composer.lock +/examples/config.php +/nbproject/private/ +/vendor/ diff --git a/vendor/google/recaptcha/.travis.yml b/vendor/google/recaptcha/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..a6257959e31bc3a8b3adebe708cd2b3c00f13174 --- /dev/null +++ b/vendor/google/recaptcha/.travis.yml @@ -0,0 +1,33 @@ +dist: trusty + +language: php + +sudo: false + +php: + - '5.5' + - '5.6' + - '7.0' + - '7.1' + - '7.2' + - '7.3' + +before_script: + - composer install + - phpenv version-name | grep ^5.[34] && echo "extension=apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini ; true + - phpenv version-name | grep ^5.[34] && echo "apc.enable_cli=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini ; true + +script: + - 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/vendor/google/recaptcha/ARCHITECTURE.md b/vendor/google/recaptcha/ARCHITECTURE.md new file mode 100644 index 0000000000000000000000000000000000000000..13add26535d710661af4027bf6b4f84cc7ea767a --- /dev/null +++ b/vendor/google/recaptcha/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/vendor/google/recaptcha/CONTRIBUTING.md b/vendor/google/recaptcha/CONTRIBUTING.md new file mode 100644 index 0000000000000000000000000000000000000000..a23686249715a134cb4779f64463e7448d803b1a --- /dev/null +++ b/vendor/google/recaptcha/CONTRIBUTING.md @@ -0,0 +1,49 @@ +# Contributing + +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 (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: + +```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. diff --git a/vendor/google/recaptcha/LICENSE b/vendor/google/recaptcha/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d147b35b3cd0cb969fe651a0e481b6f522eb7ea6 --- /dev/null +++ b/vendor/google/recaptcha/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2019, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/google/recaptcha/README.md b/vendor/google/recaptcha/README.md new file mode 100644 index 0000000000000000000000000000000000000000..92e8deae7bdf3b848be4cb80049e87fdbdcf6e96 --- /dev/null +++ b/vendor/google/recaptcha/README.md @@ -0,0 +1,140 @@ +# 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) + +reCAPTCHA is a free CAPTCHA service that protects websites from spam and abuse. +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 +- Hosted demo: https://recaptcha-demo.appspot.com/ +- Version: 1.2.4 +- License: BSD, see [LICENSE](LICENSE) + +## Installation + +### Composer (recommended) + +Use [Composer](https://getcomposer.org) to install this library from Packagist: +[`google/recaptcha`](https://packagist.org/packages/google/recaptcha) + +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.2" +} +``` + +### Direct download + +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_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 can also use your +own autoloader or require the needed files directly in your code. + +## Usage + +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. + +Then follow the [integration guide on the developer +site](https://developers.google.com/recaptcha/intro) to add the reCAPTCHA +functionality into your frontend. + +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: + +```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 threshold 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: + +```php +<?php +$recaptcha = new \ReCaptcha\ReCaptcha($secret); +$resp = $recaptcha->setExpectedHostname('recaptcha-demo.appspot.com') + ->setExpectedAction('homepage') + ->setScoreThreshold(0.5) + ->verify($gRecaptchaResponse, $remoteIp); + +if ($resp->isSuccess()) { + // Verified! +} else { + $errors = $resp->getErrorCodes(); +} +``` + +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). + +### Examples + +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 +``` + +This makes use of the in-built PHP dev server to host the examples at +http://localhost:8080/ + +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 + +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/vendor/google/recaptcha/app.yaml b/vendor/google/recaptcha/app.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b6ccaf18b74931295059544e9b1ca1ef0ae13c45 --- /dev/null +++ b/vendor/google/recaptcha/app.yaml @@ -0,0 +1,8 @@ +runtime: php +env: flex + +skip_files: +- tests + +runtime_config: + document_root: examples diff --git a/vendor/google/recaptcha/composer.json b/vendor/google/recaptcha/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..ab6b4f1c0427a13a14ac437d3401e84f32c54915 --- /dev/null +++ b/vendor/google/recaptcha/composer.json @@ -0,0 +1,39 @@ +{ + "name": "google/recaptcha", + "description": "Client library for reCAPTCHA, a free service that protects websites from spam and abuse.", + "type": "library", + "keywords": ["recaptcha", "captcha", "spam", "abuse"], + "homepage": "https://www.google.com/recaptcha/", + "license": "BSD-3-Clause", + "support": { + "forum": "https://groups.google.com/forum/#!forum/recaptcha", + "source": "https://github.com/google/recaptcha" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36|^5.7.27|^6.59|^7.5.11", + "friendsofphp/php-cs-fixer": "^2.2.20|^2.15", + "php-coveralls/php-coveralls": "^2.1" + }, + "autoload": { + "psr-4": { + "ReCaptcha\\": "src/ReCaptcha" + } + }, + "extra": { + "branch-alias": { + "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/vendor/google/recaptcha/examples/appengine-https.php b/vendor/google/recaptcha/examples/appengine-https.php new file mode 100644 index 0000000000000000000000000000000000000000..039e2db65345455ba9b01bcf15ad7aaab447bfc2 --- /dev/null +++ b/vendor/google/recaptcha/examples/appengine-https.php @@ -0,0 +1,42 @@ +<?php +/** + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// 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/vendor/google/recaptcha/examples/config.php.dist b/vendor/google/recaptcha/examples/config.php.dist new file mode 100644 index 0000000000000000000000000000000000000000..4f8f14b10a03cd317a1c780c7d408a697650dd01 --- /dev/null +++ b/vendor/google/recaptcha/examples/config.php.dist @@ -0,0 +1,46 @@ +<?php +/** + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +return [ + 'v2-standard' => [ + 'site' => '', + 'secret' => '', + ], + 'v2-invisible' => [ + 'site' => '', + 'secret' => '', + ], + 'v3' => [ + 'site' => '', + 'secret' => '', + ], +]; diff --git a/vendor/google/recaptcha/examples/examples.css b/vendor/google/recaptcha/examples/examples.css new file mode 100644 index 0000000000000000000000000000000000000000..cb3647b3a6aa26403f62d20a9a0ae43d3de9599e --- /dev/null +++ b/vendor/google/recaptcha/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; +} + +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; +} + +.hidden { + display: none; +} diff --git a/vendor/google/recaptcha/examples/google0afd8760fd68f119.html b/vendor/google/recaptcha/examples/google0afd8760fd68f119.html new file mode 100644 index 0000000000000000000000000000000000000000..457c47179deefe1408beb7343f233ded996d1b3e --- /dev/null +++ b/vendor/google/recaptcha/examples/google0afd8760fd68f119.html @@ -0,0 +1 @@ +google-site-verification: google0afd8760fd68f119.html \ No newline at end of file diff --git a/vendor/google/recaptcha/examples/index.php b/vendor/google/recaptcha/examples/index.php new file mode 100644 index 0000000000000000000000000000000000000000..b715acc839385cf3f979a85f8c33f81b6299f4ff --- /dev/null +++ b/vendor/google/recaptcha/examples/index.php @@ -0,0 +1,79 @@ +<?php +/** + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +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> + <li><h2>General</h2> + <ul> + <li><a href="/recaptcha-content-security-policy.php">Content Security Policy</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/vendor/google/recaptcha/examples/recaptcha-content-security-policy.php b/vendor/google/recaptcha/examples/recaptcha-content-security-policy.php new file mode 100644 index 0000000000000000000000000000000000000000..aaf7eb27369f7a89fb87a8b9e18787be9f8427b3 --- /dev/null +++ b/vendor/google/recaptcha/examples/recaptcha-content-security-policy.php @@ -0,0 +1,152 @@ +<?php +/** + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +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'; + +// This example shows the use of a Content Security Policy +// https://developers.google.com/web/fundamentals/security/csp/ + +// First we generate a pseudorandom nonce for each included or inline script +$nonce = base64_encode(openssl_random_pseudo_bytes(16)); + +// Send the CSP header +// Try commenting out the various lines to see what effect it has + +// NOTE: Always test your policy Content-Security-Policy-Report-Only first to +// ensure you're not blocking any critical functionality. CSP is an important +// security feature but you can break entire sections of your site if you +// implement it incorrectly. +header( + "Content-Security-Policy: " + ."default-src 'none'; " // By default we will deny everything + + ."script-src 'nonce-".$nonce."' 'strict-dynamic'; " // nonce allowing the reCAPTCHA library and other third-party scripts to be included + + ."img-src https://www.gstatic.com/recaptcha/ https://www.google-analytics.com; " // allow images from these URLS + ."frame-src https://www.google.com/; " // allow frames from this URL + + ."style-src 'self'; " // allow style from our own origin + ."connect-src 'self'; " // allow the fetch calls to our own origin +); + +// 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'; + +// The v3 API lets you provide some context for the check by specifying an action. +// See: https://developers.google.com/recaptcha/docs/v3 +$pageAction = 'examples/csp'; + +?> +<!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-content-security-policy.php"> +<script type="application/ld+json">{ "@context": "http://schema.org", "@type": "WebSite", "name": "reCAPTCHA demo - Content Security Policy", "url": "https://recaptcha-demo.appspot.com/recaptcha-content-security-policy.php" }</script> +<meta name="description" content="reCAPTCHA demo - Content Security Policy" /> +<meta property="og:url" content="https://recaptcha-demo.appspot.com/recaptcha-content-security-policy.php" /> +<meta property="og:type" content="website" /> +<meta property="og:title" content="reCAPTCHA demo - Content Security Policy" /> +<meta property="og:description" content="reCAPTCHA demo - Content Security Policy" /> +<link rel="stylesheet" type="text/css" href="/examples.css"> +<title>reCAPTCHA demo - Content Security Policy</title> +<header> + <h1>reCAPTCHA demo</h1><h2>Content Security Policy</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: + ?> + <p>This example is sending the <kbd>Content-Security-Policy</kbd> header. Look at the source and inspect the network tab for this request to see what's happening. The reCAPTCHA v3 API is being called here, however you can use the same approach for the v2 API calls as well.</p> + <p><strong>NOTE:</strong>This is a sample implementation, the score returned here is not a reflection on your Google account or type of traffic. In production, refer to the distribution of scores shown in <a href="https://www.google.com/recaptcha/admin" target="_blank">your admin interface</a> and adjust your own threshold accordingly. <strong>Do not raise issues regarding the score you see here.</strong></p> + <ol id="recaptcha-steps"> + <li class="step0">reCAPTCHA script loading</li> + <li class="step1 hidden"><kbd>grecaptcha.ready()</kbd> fired, calling <pre>grecaptcha.execute('<?php echo $siteKey; ?>', {action: '<?php echo $pageAction; ?>'})'</pre></li> + <li class="step2 hidden">Received token from reCAPTCHA service, sending to our backend with: + <pre class="token">fetch('/recaptcha-v3-verify.php?token=abc123</pre></li> + <li class="step3 hidden">Received response from our backend: <pre class="response">{"json": "from-backend"}</pre></li> + </ol> + <p><a href="/recaptcha-content-security-policy.php">⤴️ Try again</a></p> + + <!-- Add the nonce for our inline script to this tag --> + <script nonce="<?php echo $nonce; ?>"> + var onloadCallback = function() { + const steps = document.getElementById('recaptcha-steps'); + grecaptcha.ready(function() { + document.querySelector('.step1').classList.remove('hidden'); + grecaptcha.execute('<?php echo $siteKey; ?>', {action: '<?php echo $pageAction; ?>'}).then(function(token) { + document.querySelector('.token').innerHTML = 'fetch(\'/recaptcha-v3-verify.php?action=<?php echo $pageAction; ?>&token=\'' + token; + document.querySelector('.step2').classList.remove('hidden'); + + fetch('/recaptcha-v3-verify.php?action=<?php echo $pageAction; ?>&token='+token).then(function(response) { + response.json().then(function(data) { + document.querySelector('.response').innerHTML = JSON.stringify(data, null, 2); + document.querySelector('.step3').classList.remove('hidden'); + }); + }); + }); + }); + }; + </script> + <!-- Add the nonce value for the reCAPTCHA library to its script tag --> + <script async defer src="https://www.google.com/recaptcha/api.js?render=<?php echo $siteKey; ?>&onload=onloadCallback" nonce="<?php echo $nonce; ?>"></script> + +<?php +endif;?> +</main> + +<!-- Google Analytics - adding nonces here for the library and the inline code --> +<script async defer src="https://www.googletagmanager.com/gtag/js?id=UA-123057962-1" nonce="<?php echo $nonce; ?>"></script> +<script async nonce="<?php echo $nonce; ?>">window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-123057962-1');</script> diff --git a/vendor/google/recaptcha/examples/recaptcha-v2-checkbox-explicit.php b/vendor/google/recaptcha/examples/recaptcha-v2-checkbox-explicit.php new file mode 100644 index 0000000000000000000000000000000000000000..fb429a27d6b50a97af714a7642e6175023d43b41 --- /dev/null +++ b/vendor/google/recaptcha/examples/recaptcha-v2-checkbox-explicit.php @@ -0,0 +1,148 @@ +<?php +/** + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +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/vendor/google/recaptcha/examples/recaptcha-v2-checkbox.php b/vendor/google/recaptcha/examples/recaptcha-v2-checkbox.php new file mode 100644 index 0000000000000000000000000000000000000000..9395d592f76f22d0053b3ac589eade9a6e553cfd --- /dev/null +++ b/vendor/google/recaptcha/examples/recaptcha-v2-checkbox.php @@ -0,0 +1,139 @@ +<?php +/** + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +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/vendor/google/recaptcha/examples/recaptcha-v2-invisible.php b/vendor/google/recaptcha/examples/recaptcha-v2-invisible.php new file mode 100644 index 0000000000000000000000000000000000000000..c3b939788acff0cf7e736dec1119bb7ffb0ddfc8 --- /dev/null +++ b/vendor/google/recaptcha/examples/recaptcha-v2-invisible.php @@ -0,0 +1,141 @@ +<?php +/** + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +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/vendor/google/recaptcha/examples/recaptcha-v3-request-scores.php b/vendor/google/recaptcha/examples/recaptcha-v3-request-scores.php new file mode 100644 index 0000000000000000000000000000000000000000..d9430bb004dfb8340edd8e9a4874020a0bcb6203 --- /dev/null +++ b/vendor/google/recaptcha/examples/recaptcha-v3-request-scores.php @@ -0,0 +1,120 @@ +<?php +/** + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +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'; + +// The v3 API lets you provide some context for the check by specifying an action. +// See: https://developers.google.com/recaptcha/docs/v3 +$pageAction = 'examples/v3scores'; + +?> +<!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-v3-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-v3-request-scores.php" }</script> +<meta name="description" content="reCAPTCHA demo - Request scores" /> +<meta property="og:url" content="https://recaptcha-demo.appspot.com/recaptcha-v3-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>The reCAPTCHA v3 API provides a confidence score for each request.</p> + <p><strong>NOTE:</strong>This is a sample implementation, the score returned here is not a reflection on your Google account or type of traffic. In production, refer to the distribution of scores shown in <a href="https://www.google.com/recaptcha/admin" target="_blank">your admin interface</a> and adjust your own threshold accordingly. <strong>Do not raise issues regarding the score you see here.</strong></p> + <ol id="recaptcha-steps"> + <li class="step0">reCAPTCHA script loading</li> + <li class="step1 hidden"><kbd>grecaptcha.ready()</kbd> fired, calling <pre>grecaptcha.execute('<?php echo $siteKey; ?>', {action: '<?php echo $pageAction; ?>'})'</pre></li> + <li class="step2 hidden">Received token from reCAPTCHA service, sending to our backend with: + <pre class="token">fetch('/recaptcha-v3-verify.php?token=abc123</pre></li> + <li class="step3 hidden">Received response from our backend: <pre class="response">{"json": "from-backend"}</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').classList.remove('hidden'); + grecaptcha.execute('<?php echo $siteKey; ?>', {action: '<?php echo $pageAction; ?>'}).then(function(token) { + document.querySelector('.token').innerHTML = 'fetch(\'/recaptcha-v3-verify.php?action=<?php echo $pageAction; ?>&token=\'' + token; + document.querySelector('.step2').classList.remove('hidden'); + + fetch('/recaptcha-v3-verify.php?action=<?php echo $pageAction; ?>&token='+token).then(function(response) { + response.json().then(function(data) { + document.querySelector('.response').innerHTML = JSON.stringify(data, null, 2); + document.querySelector('.step3').classList.remove('hidden'); + }); + }); + }); + }); + </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/vendor/google/recaptcha/examples/recaptcha-v3-verify.php b/vendor/google/recaptcha/examples/recaptcha-v3-verify.php new file mode 100644 index 0000000000000000000000000000000000000000..3b6517a50eddfa18740978c35bc80792037337b5 --- /dev/null +++ b/vendor/google/recaptcha/examples/recaptcha-v3-verify.php @@ -0,0 +1,59 @@ +<?php +/** + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +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 +// In production, always sanitize and validate the input you retrieve from the request. +$recaptcha = new \ReCaptcha\ReCaptcha($secret); +$resp = $recaptcha->setExpectedHostname($_SERVER['SERVER_NAME']) + ->setExpectedAction($_GET['action']) + ->setScoreThreshold(0.5) + ->verify($_GET['token'], $_SERVER['REMOTE_ADDR']); +header('Content-type:application/json'); +echo json_encode($resp->toArray()); diff --git a/vendor/google/recaptcha/examples/robots.txt b/vendor/google/recaptcha/examples/robots.txt new file mode 100644 index 0000000000000000000000000000000000000000..eb0536286f3081c6c0646817037faf5446e3547d --- /dev/null +++ b/vendor/google/recaptcha/examples/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/vendor/google/recaptcha/phpunit.xml.dist b/vendor/google/recaptcha/phpunit.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..ae8661044bc57b6ecc77d3226531bb59fcd34463 --- /dev/null +++ b/vendor/google/recaptcha/phpunit.xml.dist @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.4/phpunit.xsd" + colors="true" + verbose="true" + bootstrap="src/autoload.php"> + <testsuites> + <testsuite name="reCAPTCHA Test Suite"> + <directory>tests/ReCaptcha/</directory> + </testsuite> + </testsuites> + <filter> + <whitelist> + <directory suffix=".php">src/ReCaptcha/</directory> + </whitelist> + </filter> + <logging> + <log type="coverage-clover" target="build/logs/clover.xml"/> + </logging> +</phpunit> diff --git a/vendor/google/recaptcha/src/ReCaptcha/ReCaptcha.php b/vendor/google/recaptcha/src/ReCaptcha/ReCaptcha.php new file mode 100644 index 0000000000000000000000000000000000000000..31ec44a0757abfb2e8a0c1e5d12f4e05d3e4c645 --- /dev/null +++ b/vendor/google/recaptcha/src/ReCaptcha/ReCaptcha.php @@ -0,0 +1,269 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha; + +/** + * reCAPTCHA client. + */ +class ReCaptcha +{ + /** + * Version of this client library. + * @const string + */ + const VERSION = 'php_1.2.4'; + + /** + * URL for reCAPTCHA siteverify 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. + * @var string + */ + private $secret; + + /** + * Method used to communicate with service. Defaults to POST request. + * @var RequestMethod + */ + private $requestMethod; + + /** + * Create a configured instance to use the reCAPTCHA service. + * + * @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 + */ + public function __construct($secret, RequestMethod $requestMethod = null) + { + if (empty($secret)) { + throw new \RuntimeException('No secret provided'); + } + + if (!is_string($secret)) { + throw new \RuntimeException('The provided secret must be a string'); + } + + $this->secret = $secret; + $this->requestMethod = (is_null($requestMethod)) ? new RequestMethod\Post() : $requestMethod; + } + + /** + * Calls the reCAPTCHA siteverify API to verify whether the user passes + * CAPTCHA test and additionally runs any specified additional checks + * + * @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. + */ + public function verify($response, $remoteIp = null) + { + // Discard empty solution submissions + if (empty($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); + $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/vendor/google/recaptcha/src/ReCaptcha/RequestMethod.php b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod.php new file mode 100644 index 0000000000000000000000000000000000000000..0a2a6716eaa8abdb13ae6bd74ce10eedb1582c4d --- /dev/null +++ b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod.php @@ -0,0 +1,50 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha; + +/** + * Method used to send the request to the service. + */ +interface RequestMethod +{ + + /** + * Submit the request with the specified parameters. + * + * @param RequestParameters $params Request parameters + * @return string Body of the reCAPTCHA response + */ + public function submit(RequestParameters $params); +} diff --git a/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php new file mode 100644 index 0000000000000000000000000000000000000000..eb9984253db5e851cafb94843a6efa22569f536b --- /dev/null +++ b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Curl.php @@ -0,0 +1,82 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha\RequestMethod; + +/** + * Convenience wrapper around the cURL functions to allow mocking. + */ +class Curl +{ + + /** + * @see http://php.net/curl_init + * @param string $url + * @return resource cURL handle + */ + public function init($url = null) + { + return curl_init($url); + } + + /** + * @see http://php.net/curl_setopt_array + * @param resource $ch + * @param array $options + * @return bool + */ + public function setoptArray($ch, array $options) + { + return curl_setopt_array($ch, $options); + } + + /** + * @see http://php.net/curl_exec + * @param resource $ch + * @return mixed + */ + public function exec($ch) + { + return curl_exec($ch); + } + + /** + * @see http://php.net/curl_close + * @param resource $ch + */ + public function close($ch) + { + curl_close($ch); + } +} diff --git a/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php new file mode 100644 index 0000000000000000000000000000000000000000..1bd7cce52dce316bcf153aeff653ec17583d8223 --- /dev/null +++ b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/CurlPost.php @@ -0,0 +1,104 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha\RequestMethod; + +use ReCaptcha\ReCaptcha; +use ReCaptcha\RequestMethod; +use ReCaptcha\RequestParameters; + +/** + * Sends cURL request to the reCAPTCHA service. + * Note: this requires the cURL extension to be enabled in PHP + * @see http://php.net/manual/en/book.curl.php + */ +class CurlPost implements RequestMethod +{ + /** + * Curl connection to the reCAPTCHA service + * @var Curl + */ + private $curl; + + /** + * URL for reCAPTCHA siteverify 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 siteverify API + */ + public function __construct(Curl $curl = null, $siteVerifyUrl = null) + { + $this->curl = (is_null($curl)) ? new Curl() : $curl; + $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; + } + + /** + * Submit the cURL request with the specified parameters. + * + * @param RequestParameters $params Request parameters + * @return string Body of the reCAPTCHA response + */ + public function submit(RequestParameters $params) + { + $handle = $this->curl->init($this->siteVerifyUrl); + + $options = array( + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $params->toQueryString(), + CURLOPT_HTTPHEADER => array( + 'Content-Type: application/x-www-form-urlencoded' + ), + CURLINFO_HEADER_OUT => false, + CURLOPT_HEADER => false, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => true + ); + $this->curl->setoptArray($handle, $options); + + $response = $this->curl->exec($handle); + $this->curl->close($handle); + + if ($response !== false) { + return $response; + } + + return '{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}'; + } +} diff --git a/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php new file mode 100644 index 0000000000000000000000000000000000000000..a4ff716fb003f1039e803d5b46653004d38ab8b9 --- /dev/null +++ b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Post.php @@ -0,0 +1,88 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha\RequestMethod; + +use ReCaptcha\ReCaptcha; +use ReCaptcha\RequestMethod; +use ReCaptcha\RequestParameters; + +/** + * Sends POST requests to the reCAPTCHA service. + */ +class Post implements RequestMethod +{ + /** + * URL for reCAPTCHA siteverify API + * @var string + */ + private $siteVerifyUrl; + + /** + * Only needed if you want to override the defaults + * + * @param string $siteVerifyUrl URL for reCAPTCHA siteverify API + */ + public function __construct($siteVerifyUrl = null) + { + $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; + } + + /** + * Submit the POST request with the specified parameters. + * + * @param RequestParameters $params Request parameters + * @return string Body of the reCAPTCHA response + */ + public function submit(RequestParameters $params) + { + $options = array( + 'http' => array( + 'header' => "Content-type: application/x-www-form-urlencoded\r\n", + 'method' => 'POST', + 'content' => $params->toQueryString(), + // Force the peer to validate (not needed in 5.6.0+, but still works) + 'verify_peer' => true, + ), + ); + $context = stream_context_create($options); + $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/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php new file mode 100644 index 0000000000000000000000000000000000000000..236bd5f5ddb9cbb6057bd4085cc07297566149d4 --- /dev/null +++ b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/Socket.php @@ -0,0 +1,112 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha\RequestMethod; + +/** + * Convenience wrapper around native socket and file functions to allow for + * mocking. + */ +class Socket +{ + private $handle = null; + + /** + * fsockopen + * + * @see http://php.net/fsockopen + * @param string $hostname + * @param int $port + * @param int $errno + * @param string $errstr + * @param float $timeout + * @return resource + */ + public function fsockopen($hostname, $port = -1, &$errno = 0, &$errstr = '', $timeout = null) + { + $this->handle = fsockopen($hostname, $port, $errno, $errstr, (is_null($timeout) ? ini_get("default_socket_timeout") : $timeout)); + + if ($this->handle != false && $errno === 0 && $errstr === '') { + return $this->handle; + } + return false; + } + + /** + * fwrite + * + * @see http://php.net/fwrite + * @param string $string + * @param int $length + * @return int | bool + */ + public function fwrite($string, $length = null) + { + return fwrite($this->handle, $string, (is_null($length) ? strlen($string) : $length)); + } + + /** + * fgets + * + * @see http://php.net/fgets + * @param int $length + * @return string + */ + public function fgets($length = null) + { + return fgets($this->handle, $length); + } + + /** + * feof + * + * @see http://php.net/feof + * @return bool + */ + public function feof() + { + return feof($this->handle); + } + + /** + * fclose + * + * @see http://php.net/fclose + * @return bool + */ + public function fclose() + { + return fclose($this->handle); + } +} diff --git a/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php new file mode 100644 index 0000000000000000000000000000000000000000..464bc28d42ec4da3fe0222de0e19e21cc37ed704 --- /dev/null +++ b/vendor/google/recaptcha/src/ReCaptcha/RequestMethod/SocketPost.php @@ -0,0 +1,108 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha\RequestMethod; + +use ReCaptcha\ReCaptcha; +use ReCaptcha\RequestMethod; +use ReCaptcha\RequestParameters; + +/** + * Sends a POST request to the reCAPTCHA service, but makes use of fsockopen() + * instead of get_file_contents(). This is to account for people who may be on + * servers where allow_url_open is disabled. + */ +class SocketPost implements RequestMethod +{ + /** + * Socket to the reCAPTCHA service + * @var Socket + */ + private $socket; + + /** + * 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 siteverify API + */ + public function __construct(Socket $socket = null, $siteVerifyUrl = null) + { + $this->socket = (is_null($socket)) ? new Socket() : $socket; + $this->siteVerifyUrl = (is_null($siteVerifyUrl)) ? ReCaptcha::SITE_VERIFY_URL : $siteVerifyUrl; + } + + /** + * Submit the POST request with the specified parameters. + * + * @param RequestParameters $params Request parameters + * @return string Body of the reCAPTCHA response + */ + public function submit(RequestParameters $params) + { + $errno = 0; + $errstr = ''; + $urlParsed = parse_url($this->siteVerifyUrl); + + 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 " . $urlParsed['path'] . " HTTP/1.0\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"; + $request .= $content . "\r\n\r\n"; + + $this->socket->fwrite($request); + $response = ''; + + while (!$this->socket->feof()) { + $response .= $this->socket->fgets(4096); + } + + $this->socket->fclose(); + + if (0 !== strpos($response, 'HTTP/1.0 200 OK')) { + return '{"success": false, "error-codes": ["'.ReCaptcha::E_BAD_RESPONSE.'"]}'; + } + + $parts = preg_split("#\n\s*\n#Uis", $response); + + return $parts[1]; + } +} diff --git a/vendor/google/recaptcha/src/ReCaptcha/RequestParameters.php b/vendor/google/recaptcha/src/ReCaptcha/RequestParameters.php new file mode 100644 index 0000000000000000000000000000000000000000..e9ba45354fdef44f0fbf91bf33137276180f2b47 --- /dev/null +++ b/vendor/google/recaptcha/src/ReCaptcha/RequestParameters.php @@ -0,0 +1,111 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha; + +/** + * Stores and formats the parameters for the request to the reCAPTCHA service. + */ +class RequestParameters +{ + /** + * The shared key between your site and reCAPTCHA. + * @var string + */ + private $secret; + + /** + * The user response token provided by reCAPTCHA, verifying the user on your site. + * @var string + */ + private $response; + + /** + * Remote user's IP address. + * @var string + */ + private $remoteIp; + + /** + * Client version. + * @var string + */ + private $version; + + /** + * Initialise parameters. + * + * @param string $secret Site secret. + * @param string $response Value from g-captcha-response form field. + * @param string $remoteIp User's IP address. + * @param string $version Version of this client library. + */ + public function __construct($secret, $response, $remoteIp = null, $version = null) + { + $this->secret = $secret; + $this->response = $response; + $this->remoteIp = $remoteIp; + $this->version = $version; + } + + /** + * Array representation. + * + * @return array Array formatted parameters. + */ + public function toArray() + { + $params = array('secret' => $this->secret, 'response' => $this->response); + + if (!is_null($this->remoteIp)) { + $params['remoteip'] = $this->remoteIp; + } + + if (!is_null($this->version)) { + $params['version'] = $this->version; + } + + return $params; + } + + /** + * Query string representation for HTTP request. + * + * @return string Query string formatted parameters. + */ + public function toQueryString() + { + return http_build_query($this->toArray(), '', '&'); + } +} diff --git a/vendor/google/recaptcha/src/ReCaptcha/Response.php b/vendor/google/recaptcha/src/ReCaptcha/Response.php new file mode 100644 index 0000000000000000000000000000000000000000..55838c0742294648e7fa8169afef1be9aadaf796 --- /dev/null +++ b/vendor/google/recaptcha/src/ReCaptcha/Response.php @@ -0,0 +1,218 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha; + +/** + * The response returned from the service. + */ +class Response +{ + /** + * Success or failure. + * @var boolean + */ + private $success = false; + + /** + * Error code strings. + * @var array + */ + private $errorCodes = array(); + + /** + * The hostname of the site where the reCAPTCHA was solved. + * @var string + */ + 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. + * + * @param string $json + * @return \ReCaptcha\Response + */ + public static function fromJson($json) + { + $responseData = json_decode($json, true); + + if (!$responseData) { + 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, $challengeTs, $apkPackageName, $score, $action); + } + + if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) { + return new Response(false, $responseData['error-codes'], $hostname, $challengeTs, $apkPackageName, $score, $action); + } + + return new Response(false, array(ReCaptcha::E_UNKNOWN_ERROR), $hostname, $challengeTs, $apkPackageName, $score, $action); + } + + /** + * Constructor. + * + * @param boolean $success + * @param string $hostname + * @param string $challengeTs + * @param string $apkPackageName + * @param float $score + * @param string $action + * @param array $errorCodes + */ + public function __construct($success, array $errorCodes = array(), $hostname = null, $challengeTs = null, $apkPackageName = null, $score = null, $action = null) + { + $this->success = $success; + $this->hostname = $hostname; + $this->challengeTs = $challengeTs; + $this->apkPackageName = $apkPackageName; + $this->score = $score; + $this->action = $action; + $this->errorCodes = $errorCodes; + } + + /** + * Is success? + * + * @return boolean + */ + public function isSuccess() + { + return $this->success; + } + + /** + * Get error codes. + * + * @return array + */ + public function getErrorCodes() + { + return $this->errorCodes; + } + + /** + * Get hostname. + * + * @return string + */ + public function getHostname() + { + 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/vendor/google/recaptcha/src/autoload.php b/vendor/google/recaptcha/src/autoload.php new file mode 100644 index 0000000000000000000000000000000000000000..7947a10506ef13917e6307f89b222de0c39f75e9 --- /dev/null +++ b/vendor/google/recaptcha/src/autoload.php @@ -0,0 +1,69 @@ +<?php + +/* An autoloader for ReCaptcha\Foo classes. This should be required() + * by the user before attempting to instantiate any of the ReCaptcha + * classes. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +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; + } + + /* All of the classes have names like "ReCaptcha\Foo", so we need + * to replace the backslashes with frontslashes if we want the + * name to map directly to a location in the filesystem. + */ + $class = str_replace('\\', '/', $class); + + /* First, check under the current directory. It is important that + * we look here first, so that we don't waste time searching for + * test classes in the common case. + */ + $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 + * a test class? + */ + $path = dirname(__FILE__).'/../tests/'.$class.'.php'; + if (is_readable($path)) { + require_once $path; + } +}); diff --git a/vendor/google/recaptcha/tests/ReCaptcha/ReCaptchaTest.php b/vendor/google/recaptcha/tests/ReCaptcha/ReCaptchaTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ddb16f0962d2fd9a231200fcecdd6f569caa2d48 --- /dev/null +++ b/vendor/google/recaptcha/tests/ReCaptcha/ReCaptchaTest.php @@ -0,0 +1,198 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha; + +use PHPUnit\Framework\TestCase; + +class ReCaptchaTest extends TestCase +{ + + /** + * @expectedException \RuntimeException + * @dataProvider invalidSecretProvider + */ + public function testExceptionThrownOnInvalidSecret($invalid) + { + $rc = new ReCaptcha($invalid); + } + + public function invalidSecretProvider() + { + return array( + array(''), + array(null), + array(0), + array(new \stdClass()), + array(array()), + ); + } + + public function testVerifyReturnsErrorOnMissingResponse() + { + $rc = new ReCaptcha('secret'); + $response = $rc->verify(''); + $this->assertFalse($response->isSuccess()); + $this->assertEquals(array(Recaptcha::E_MISSING_INPUT_RESPONSE), $response->getErrorCodes()); + } + + private function getMockRequestMethod($responseJson) + { + $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; + } + + 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/vendor/google/recaptcha/tests/ReCaptcha/RequestMethod/CurlPostTest.php b/vendor/google/recaptcha/tests/ReCaptcha/RequestMethod/CurlPostTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8fb17dc0ce2ba5d481c3f91fa90148e769630dd8 --- /dev/null +++ b/vendor/google/recaptcha/tests/ReCaptcha/RequestMethod/CurlPostTest.php @@ -0,0 +1,123 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha\RequestMethod; + +use \ReCaptcha\ReCaptcha; +use \ReCaptcha\RequestParameters; +use PHPUnit\Framework\TestCase; + +class CurlPostTest extends TestCase +{ + protected function setUp() + { + if (!extension_loaded('curl')) { + $this->markTestSkipped( + 'The cURL extension is not available.' + ); + } + } + + public function testSubmit() + { + $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('RESPONSEBODY'); + $curl->expects($this->once()) + ->method('close'); + + $pc = new CurlPost($curl); + $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/vendor/google/recaptcha/tests/ReCaptcha/RequestMethod/PostTest.php b/vendor/google/recaptcha/tests/ReCaptcha/RequestMethod/PostTest.php new file mode 100644 index 0000000000000000000000000000000000000000..bdfb78ee75c1d96154f337ee2ae6018748018a00 --- /dev/null +++ b/vendor/google/recaptcha/tests/ReCaptcha/RequestMethod/PostTest.php @@ -0,0 +1,149 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha\RequestMethod; + +use \ReCaptcha\ReCaptcha; +use ReCaptcha\RequestParameters; +use PHPUnit\Framework\TestCase; + +class PostTest extends TestCase +{ + public static $assert = null; + protected $parameters = null; + protected $runcount = 0; + + public function setUp() + { + $this->parameters = new RequestParameters('secret', 'response', 'remoteip', 'version'); + } + + public function tearDown() + { + self::$assert = null; + } + + public function testHTTPContextOptions() + { + $req = new Post(); + self::$assert = array($this, 'httpContextOptionsCallback'); + $req->submit($this->parameters); + $this->assertEquals(1, $this->runcount, 'The assertion was ran'); + } + + public function testSSLContextOptions() + { + $req = new Post(); + self::$assert = array($this, 'sslContextOptionsCallback'); + $req->submit($this->parameters); + $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) + { + $this->runcount++; + $this->assertCommonOptions($args); + + $options = stream_context_get_options($args[2]); + $this->assertArrayHasKey('http', $options); + + $this->assertArrayHasKey('method', $options['http']); + $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', + ); + foreach ($headers as $header) { + $this->assertContains($header, $options['http']['header']); + } + } + + public function sslContextOptionsCallback(array $args) + { + $this->runcount++; + $this->assertCommonOptions($args); + + $options = stream_context_get_options($args[2]); + $this->assertArrayHasKey('http', $options); + $this->assertArrayHasKey('verify_peer', $options['http']); + $this->assertTrue($options['http']['verify_peer']); + } + + protected function assertCommonOptions(array $args) + { + $this->assertCount(3, $args); + $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'); + } +} + +function file_get_contents() +{ + if (PostTest::$assert) { + return call_user_func(PostTest::$assert, func_get_args()); + } + // Since we can't represent maxlen in userland... + return call_user_func_array('file_get_contents', func_get_args()); +} diff --git a/vendor/google/recaptcha/tests/ReCaptcha/RequestMethod/SocketPostTest.php b/vendor/google/recaptcha/tests/ReCaptcha/RequestMethod/SocketPostTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d9e1ede2acdc7c06cd4e21cfc30ec40862ed550d --- /dev/null +++ b/vendor/google/recaptcha/tests/ReCaptcha/RequestMethod/SocketPostTest.php @@ -0,0 +1,136 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha\RequestMethod; + +use ReCaptcha\ReCaptcha; +use ReCaptcha\RequestParameters; +use PHPUnit\Framework\TestCase; + +class SocketPostTest extends TestCase +{ + public function testSubmitSuccess() + { + $socket = $this->getMockBuilder(\ReCaptcha\RequestMethod\Socket::class) + ->disableOriginalConstructor() + ->setMethods(array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose')) + ->getMock(); + $socket->expects($this->once()) + ->method('fsockopen') + ->willReturn(true); + $socket->expects($this->once()) + ->method('fwrite'); + $socket->expects($this->once()) + ->method('fgets') + ->willReturn("HTTP/1.0 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); + $response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version")); + $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.0 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->getMockBuilder(\ReCaptcha\RequestMethod\Socket::class) + ->disableOriginalConstructor() + ->setMethods(array('fsockopen', 'fwrite', 'fgets', 'feof', 'fclose')) + ->getMock(); + $socket->expects($this->once()) + ->method('fsockopen') + ->willReturn(true); + $socket->expects($this->once()) + ->method('fwrite'); + $socket->expects($this->once()) + ->method('fgets') + ->willReturn("HTTP/1.0 500 NOPEn\\nBOBBINS"); + $socket->expects($this->exactly(2)) + ->method('feof') + ->will($this->onConsecutiveCalls(false, true)); + $socket->expects($this->once()) + ->method('fclose') + ->willReturn(true); + + $ps = new SocketPost($socket); + $response = $ps->submit(new RequestParameters("secret", "response", "remoteip", "version")); + $this->assertEquals('{"success": false, "error-codes": ["'.ReCaptcha::E_BAD_RESPONSE.'"]}', $response); + } + + public function testConnectionFailureReturnsError() + { + $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('{"success": false, "error-codes": ["'.ReCaptcha::E_CONNECTION_FAILED.'"]}', $response); + } +} diff --git a/vendor/google/recaptcha/tests/ReCaptcha/RequestParametersTest.php b/vendor/google/recaptcha/tests/ReCaptcha/RequestParametersTest.php new file mode 100644 index 0000000000000000000000000000000000000000..fafded2e36da8eb0fada1a6ea78369720d6421cd --- /dev/null +++ b/vendor/google/recaptcha/tests/ReCaptcha/RequestParametersTest.php @@ -0,0 +1,70 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha; + +use PHPUnit\Framework\TestCase; + +class RequestParametersTest extends Testcase +{ + public function provideValidData() + { + return array( + array('SECRET', 'RESPONSE', 'REMOTEIP', 'VERSION', + array('secret' => 'SECRET', 'response' => 'RESPONSE', 'remoteip' => 'REMOTEIP', 'version' => 'VERSION'), + 'secret=SECRET&response=RESPONSE&remoteip=REMOTEIP&version=VERSION'), + array('SECRET', 'RESPONSE', null, null, + array('secret' => 'SECRET', 'response' => 'RESPONSE'), + 'secret=SECRET&response=RESPONSE'), + ); + } + + /** + * @dataProvider provideValidData + */ + public function testToArray($secret, $response, $remoteIp, $version, $expectedArray, $expectedQuery) + { + $params = new RequestParameters($secret, $response, $remoteIp, $version); + $this->assertEquals($params->toArray(), $expectedArray); + } + + /** + * @dataProvider provideValidData + */ + public function testToQueryString($secret, $response, $remoteIp, $version, $expectedArray, $expectedQuery) + { + $params = new RequestParameters($secret, $response, $remoteIp, $version); + $this->assertEquals($params->toQueryString(), $expectedQuery); + } +} diff --git a/vendor/google/recaptcha/tests/ReCaptcha/ResponseTest.php b/vendor/google/recaptcha/tests/ReCaptcha/ResponseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7894c2a9273d52e2d04218d594c2fc8f684b2381 --- /dev/null +++ b/vendor/google/recaptcha/tests/ReCaptcha/ResponseTest.php @@ -0,0 +1,173 @@ +<?php +/** + * This is a PHP library that handles calling reCAPTCHA. + * + * BSD 3-Clause License + * @copyright (c) 2019, Google Inc. + * @link https://www.google.com/recaptcha + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +namespace ReCaptcha; + +use PHPUnit\Framework\TestCase; + +class ResponseTest extends TestCase +{ + + /** + * @dataProvider provideJson + */ + 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, 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, + ), + ); + } + + public function testIsSuccess() + { + $response = new Response(true); + $this->assertTrue($response->isSuccess()); + + $response = new Response(false); + $this->assertFalse($response->isSuccess()); + + $response = new Response(true, array(), 'example.com'); + $this->assertEquals('example.com', $response->getHostName()); + } + + public function testGetErrorCodes() + { + $errorCodes = array('test'); + $response = new Response(true, $errorCodes); + $this->assertEquals($errorCodes, $response->getErrorCodes()); + } + + public function testGetHostname() + { + $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/web/modules/recaptcha_v3/LICENSE.txt b/web/modules/recaptcha_v3/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..d159169d1050894d3ea3b98e1c965c4058208fe1 --- /dev/null +++ b/web/modules/recaptcha_v3/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/web/modules/recaptcha_v3/README.md b/web/modules/recaptcha_v3/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6633c129dbd46980487a832f113fd661f2740add --- /dev/null +++ b/web/modules/recaptcha_v3/README.md @@ -0,0 +1,88 @@ +CONTENTS OF THIS FILE +--------------------- + + * Introduction + * Requirements + * Recommended modules + * Installation + * Configuration + * Maintainers + +INTRODUCTION +------------ + +This module enables you to easily configure reCaptcha v3 +and a fallback challenge (captcha/recaptcha v2 e.g). +In case user fails reCaptcha v3, +he can be prompted with an additional challenge to prove. +This is an ideal way to maximize security without any user friction. + +We no more rely on the reCAPTCHA module for the use of the `recaptcha-php` +library which is included in this module, and make use of +Composer instead of keeping a duplicating code. + + * For a full description of the module, visit the project page: + https://www.drupal.org/project/admin_menu + + * To submit bug reports and feature suggestions, or track changes: + https://www.drupal.org/project/issues/admin_menu + +REQUIREMENTS +------------ + +This module requires the following module: + + * Captcha (https://www.drupal.org/project/captcha) + +This module requires the following library: + + * google/recaptacha (https://github.com/google/recaptcha) + +RECOMMENDED MODULES +------------------- + + * reCAPTCHA (https://www.drupal.org/project/recaptcha): + When enabled, reCAPTCHA v2 becomes available as fallback challenge. + +INSTALLATION +------------ + + * Install as you would normally install a contributed Drupal module. Visit + https://www.drupal.org/documentation/install/modules-themes/modules-8 + for further information. + + * If not using Composer, + install the google/recaptacha (https://github.com/google/recaptcha) library. + +CONFIGURATION +------------- + + * Register reCAPTCHA v3 keys (https://www.google.com/recaptcha/admin/create). + + - The documentation for Google reCaptcha V3 + + The documentation can be found here + https://developers.google.com/recaptcha/docs/v3), + with information regarding keys registration. + + * Create at least one action: + + - Populate action name + + - Choose score threshold + + - Select action on user verification fail + + * Use the action you created above as a challenge in captcha form settings. + +MAINTAINERS +----------- + +Current maintainers: + * Denis (dench0) - https://www.drupal.org/u/dench0 + * Majid Ali Khan (majid.ali) - https://www.drupal.org/u/majidali + * Fabien Leroux (B-Prod) - https://www.drupal.org/u/b-prod + + +The development of Drupal 8 version of this project has been sponsored by: + * 1xINTERNET diff --git a/web/modules/recaptcha_v3/composer.json b/web/modules/recaptcha_v3/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..ae49e614392b807b41c6ef49feab9ecf8d7c6e71 --- /dev/null +++ b/web/modules/recaptcha_v3/composer.json @@ -0,0 +1,11 @@ +{ + "name": "drupal/recaptcha_v3", + "description": "The reCaptcha V3 module provides integration with Google reCaptcha V3 and CAPTCHA module.", + "type": "drupal-module", + "license": "GPL-2.0+", + "minimum-stability": "dev", + "require": { + "google/recaptcha": "^1.2", + "drupal/captcha": "^1.0.0-beta4" + } +} diff --git a/web/modules/recaptcha_v3/config/install/recaptcha_v3.settings.yml b/web/modules/recaptcha_v3/config/install/recaptcha_v3.settings.yml new file mode 100644 index 0000000000000000000000000000000000000000..0069e5b48d8a5d9d7e9d6fabf6a12d077e7b822e --- /dev/null +++ b/web/modules/recaptcha_v3/config/install/recaptcha_v3.settings.yml @@ -0,0 +1,6 @@ +site_key: '' +secret_key: '' +verify_hostname: true +default_challenge: 'captcha\Math' +error_message: 'Antibot verification failed.' +cacheable: false diff --git a/web/modules/recaptcha_v3/config/schema/recaptcha_v3.schema.yml b/web/modules/recaptcha_v3/config/schema/recaptcha_v3.schema.yml new file mode 100644 index 0000000000000000000000000000000000000000..38f6112f208dc820e7eb6676d31d45a1f1444f1a --- /dev/null +++ b/web/modules/recaptcha_v3/config/schema/recaptcha_v3.schema.yml @@ -0,0 +1,43 @@ +recaptcha_v3.settings: + type: config_object + label: 'reCAPTCHA v3 settings' + mapping: + site_key: + type: string + label: 'The site key' + secret_key: + type: string + label: 'The secret key' + verify_hostname: + type: boolean + label: 'Checks the hostname' + default_challenge: + type: string + label: 'Default fallback challenge' + error_message: + type: string + label: 'Error message' + translatable: true + cacheable: + type: boolean + label: 'Make captcha cacheble' + +recaptcha_v3.recaptcha_v3_action.*: + type: config_entity + label: 'reCAPTCHA v3 action config' + mapping: + id: + type: string + label: 'ID' + label: + type: label + label: 'Label' + threshold: + type: float + label: 'Threshold' + challenge: + type: string + label: 'Challenge' + uuid: + type: string + label: 'UUID' \ No newline at end of file diff --git a/web/modules/recaptcha_v3/js/recaptcha_v3.js b/web/modules/recaptcha_v3/js/recaptcha_v3.js new file mode 100644 index 0000000000000000000000000000000000000000..4ce96882fffadab59508632e7179b5a66d0223df --- /dev/null +++ b/web/modules/recaptcha_v3/js/recaptcha_v3.js @@ -0,0 +1,38 @@ +/** + * @file + * Recaptcha v3 behaviors. + */ + +(function ($, Drupal) { + 'use strict'; + + /** + * Attach recaptcha response token from google with form. + * + * @type {{attach: Drupal.behaviors.reCaptchaV3.attach}} + */ + Drupal.behaviors.reCaptchaV3 = { + attach: function (context) { + $('.recaptcha-v3-token', context).once('recaptcha-v3-token').each(function () { + var $token_element = $(this); + var timer = setInterval(function() { + if (grecaptcha !== undefined) { + grecaptcha.ready(function () { + grecaptcha.execute( + $token_element.data('recaptchaV3SiteKey'), + { + action: $token_element.data('recaptchaV3Action') + } + ).then(function (token) { + $token_element.val(token); + $token_element.trigger('change'); + }); + }); + clearInterval(timer) + } + }, 500); + }); + } + }; + +})(jQuery, Drupal); diff --git a/web/modules/recaptcha_v3/recaptcha_v3.info.yml b/web/modules/recaptcha_v3/recaptcha_v3.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..68f5329c751d2f1961c7d3ef4658b2e667f87ff9 --- /dev/null +++ b/web/modules/recaptcha_v3/recaptcha_v3.info.yml @@ -0,0 +1,14 @@ +name: 'reCAPTCHA v3' +type: module +description: 'The reCaptcha V3 module provides integration with Google reCaptcha V3 and CAPTCHA module.' +core: 8.x +core_version_requirement: ^8 || ^9 +package: Spam control +configure: recaptcha_v3.settings +dependencies: + - captcha:captcha + +# Information added by Drupal.org packaging script on 2020-05-25 +version: '8.x-1.3' +project: 'recaptcha_v3' +datestamp: 1590393629 diff --git a/web/modules/recaptcha_v3/recaptcha_v3.libraries.yml b/web/modules/recaptcha_v3/recaptcha_v3.libraries.yml new file mode 100644 index 0000000000000000000000000000000000000000..5af1cb916aa978bc6c89d79f6f680025bbf4e7b6 --- /dev/null +++ b/web/modules/recaptcha_v3/recaptcha_v3.libraries.yml @@ -0,0 +1,18 @@ +google.recaptcha: + remote: https://github.com/google/recaptcha + version: "1.2.1" + license: + name: BSD + url: https://github.com/google/recaptcha/blob/master/LICENSE + gpl-compatible: true + js: + https://www.google.com/recaptcha/api.js: { type: external, minified: true, weight: -200, attributes: { defer: true, async: true } } + +recaptcha_v3: + version: VERSION + js: + js/recaptcha_v3.js: { weight: 0 } + dependencies: + - core/drupal + - core/jquery.once + - recaptcha_v3/google.recaptcha diff --git a/web/modules/recaptcha_v3/recaptcha_v3.links.action.yml b/web/modules/recaptcha_v3/recaptcha_v3.links.action.yml new file mode 100644 index 0000000000000000000000000000000000000000..47d1726da6c855fd71ba475d2115cf4e2b4c9caf --- /dev/null +++ b/web/modules/recaptcha_v3/recaptcha_v3.links.action.yml @@ -0,0 +1,5 @@ +entity.recaptcha_v3_action.add_form: + route_name: entity.recaptcha_v3_action.add_form + title: 'Add reCAPTCHA v3 action' + appears_on: + - entity.recaptcha_v3_action.collection diff --git a/web/modules/recaptcha_v3/recaptcha_v3.links.task.yml b/web/modules/recaptcha_v3/recaptcha_v3.links.task.yml new file mode 100644 index 0000000000000000000000000000000000000000..cd98c0dba89e7f2d0962793899010cc5c428c7c6 --- /dev/null +++ b/web/modules/recaptcha_v3/recaptcha_v3.links.task.yml @@ -0,0 +1,11 @@ +recaptcha_v3.settings: + route_name: recaptcha_v3.settings + title: reCAPTCHA v3 + base_route: captcha_settings + weight: 9 + +entity.recaptcha_v3_action.collection: + route_name: entity.recaptcha_v3_action.collection + title: reCAPTCHA v3 actions + base_route: captcha_settings + weight: 10 diff --git a/web/modules/recaptcha_v3/recaptcha_v3.module b/web/modules/recaptcha_v3/recaptcha_v3.module new file mode 100644 index 0000000000000000000000000000000000000000..ffb42e8c83fff445082e0749866f70e0c22efdf0 --- /dev/null +++ b/web/modules/recaptcha_v3/recaptcha_v3.module @@ -0,0 +1,332 @@ +<?php + +/** + * @file + * Contains recaptcha_v3.module. + */ + +use Drupal\Component\Utility\Html; +use Drupal\Core\Ajax\AjaxResponse; +use Drupal\Core\Ajax\ReplaceCommand; +use Drupal\recaptcha_v3\Entity\ReCaptchaV3Action; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\recaptcha_v3\ReCaptchaV3ActionInterface; +use ReCaptcha\ReCaptcha; + +/** + * Implements hook_help(). + */ +function recaptcha_v3_help($route_name, RouteMatchInterface $route_match) { + switch ($route_name) { + // Main module help for the recaptcha_v3 module. + case 'help.page.recaptcha_v3': + $output = ''; + $output .= '<h3>' . t('About') . '</h3>'; + $output .= '<p>' . t('reCAPTCHA v3 returns a score for each request without user friction. The score is based on interactions with your site and enables you to take an appropriate action for your site.') . '</p>'; + $output .= '<h3>' . t('Configuration') . '</h3>'; + $output .= '<p>' . t('At first you need to create at least one action: populate action name<sup>1</sup>, choose score threshold<sup>2</sup> and select action on user verification fail<sup>3</sup>.') . '</p>'; + $output .= '<ol>'; + $output .= '<li>' . t('reCAPTCHA v3 introduces a new concept: actions. Actions name will be displayed in detailed break-down of data for your top ten actions in the <a href="@admin_console_link" target="_blank">admin console</a>', ['@admin_console_link' => 'https://g.co/recaptcha/admin']) . '</li>'; + $output .= '<li>' . t('reCAPTCHA v3 returns a score (1.0 is very likely a good interaction, 0.0 is very likely a bot). Based on the score, you can take variable action in the context of your site.') . '</li>'; + $output .= '<li>' . t('You could specify additional validation challenge, for failed recaptcha v3 validations. If you leave empty "Default challenge type" and "Challenge" for concrete action, user could not submit form if his validation failed.') . '</li>'; + $output .= '</ol>'; + return $output; + } +} + +/** + * Implements hook_library_info_alter(). + */ +function recaptcha_v3_library_info_alter(&$libraries, $extension) { + if ($extension === 'recaptcha_v3' && isset($libraries['google.recaptcha'])) { + $api_url = array_keys($libraries['google.recaptcha']['js']); + $api_url = array_shift($api_url); + $site_key = \Drupal::config('recaptcha_v3.settings')->get('site_key'); + $key = $api_url . '?render=' . $site_key; + $js = array_shift($libraries['google.recaptcha']['js']); + $libraries['google.recaptcha']['js'][$key] = $js; + } +} + +/** + * Implements hook_element_info_alter(). + * + * Place recaptcha v3 preprocess function at the beginning, so in this way + * it is possible to verify captcha and in case of fail, replace it by + * fallback captcha challenge. + * + * Add one more process function to the end to display recaptcha_v3 error if + * happens. + */ +function recaptcha_v3_element_info_alter(array &$info) { + array_unshift($info['captcha']['#process'], 'recaptcha_v3_pre_captcha_element_process'); + $info['captcha']['#process'][] = 'recaptcha_v3_post_captcha_element_process'; +} + +/** + * Perform replacing of the recaptcha element by fallback challenge. + * + * This happens in two cases: + * 1. form was submitted + * 2. performed form API ajax request. + * + * @param array $element + * The recaptcha v3 form element. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The recaptcha v3 form state. + * @param array $complete_form + * The recaptcha v3 complete form object. + * + * @return mixed + * Set fallback challenge if recaptcha v3 fail. + */ +function recaptcha_v3_pre_captcha_element_process(array &$element, FormStateInterface $form_state, array &$complete_form) { + // If form is processed input then recaptcha v3 response should be in + // form values and need replace reCAPTCHA v3 element by fallback + // challenge before captcha module lement process callback, because otherwise + // in case of error form will not rebuild and recaptcha v3 element will + // return again. + if ($form_state->isProcessingInput()) { + + \Drupal::moduleHandler()->loadInclude('captcha', 'inc', 'captcha'); + list($captcha_type_module, $captcha_type_challenge) = _captcha_parse_captcha_type($element['#captcha_type']); + + if ($captcha_type_module === 'recaptcha_v3') { + $action = ReCaptchaV3Action::load($captcha_type_challenge); + $challenge = $action ? $action->getChallenge() : 'default'; + // Replacing 'default' challenge by the real captcha challenge. + if ($challenge === 'default') { + $challenge = \Drupal::config('recaptcha_v3.settings') + ->get('default_challenge'); + } + + if ($challenge) { + $element['#captcha_type'] = $challenge; + } + + $form_state->setTemporaryValue('recaptcha_v3_action_name', $captcha_type_challenge); + } + } + return $element; +} + +/** + * Implements hook_captcha(). + */ +function recaptcha_v3_captcha($op, $captcha_type = '', $captcha_sid = NULL) { + switch ($op) { + case 'list': + return array_keys(ReCaptchaV3Action::loadMultiple()); + + case 'generate': + $captcha = []; + if ($recaptcha_v3_action = ReCaptchaV3Action::load($captcha_type)) { + $config = \Drupal::config('recaptcha_v3.settings'); + $captcha['form']['captcha_response'] = [ + '#type' => 'hidden', + '#default_value' => '', + '#attributes' => [ + // Need add id, because element should have id or + // 'selector' property should exist in #ajax array + // to attaching event for triggering ajax request. + 'id' => Html::getUniqueId('recaptcha_v3_token'), + 'class' => ['recaptcha-v3-token'], + 'data-recaptcha-v3-action' => $recaptcha_v3_action->id(), + 'data-recaptcha-v3-site-key' => $config->get('site_key'), + ], + '#attached' => [ + 'library' => [ + 'recaptcha_v3/recaptcha_v3', + ], + ], + '#ajax' => [ + 'callback' => 'recaptcha_v3_ajax_callback', + 'event' => 'change', + ], + ]; + $captcha['solution'] = TRUE; + $captcha['captcha_validate'] = 'recaptcha_v3_validate'; + $captcha['cacheable'] = (bool) $config->get('cacheable'); + } + return $captcha; + } +} + +/** + * Replace #captcha_validate by reCAPTCHA v3 validation function. + * + * If real captcha type is reCAPTCHA v3 and it was not yet verify. + * + * @param array $element + * The recaptcha v3 form element. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The recaptcha v3 form state. + * @param array $complete_form + * The recaptcha v3 complete form object. + * + * @return mixed + * Set recaptcha v3 challenge if not already validated. + */ +function recaptcha_v3_post_captcha_element_process(array &$element, FormStateInterface $form_state, array &$complete_form) { + if ($form_state->getTemporaryValue('recaptcha_v3_action_name') && !$form_state->has('recaptcha_v3')) { + $element['#captcha_validate'] = 'recaptcha_v3_validate'; + } + return $element; +} + +/** + * CAPTCHA Callback; Validates the reCAPTCHA v3 code. + */ +function recaptcha_v3_validate($solution, $captcha_response, $element, FormStateInterface $form_state) { + // reCAPTCHA v3 was verified in one of the previous request. + if ($form_state->has('recaptcha_v3')) { + return (bool) $form_state->get(['recaptcha_v3', 'success']); + } + // Using user input instead of $captcha_response variable, because stupid + // recaptcha module wrongly use 'captcha_response' form element - + // instead of '#default_value' it using '#value' form api key: + // $captcha['form']['captcha_response'] = [ + // '#type' => 'hidden', + // '#value' => 'Google no captcha', // Problem is here + // ]; + // so if using recaptcha as fallback challenge, $captcha_response is always + // have 'Google no captcha' value. + $user_input = $form_state->getUserInput(); + if (!empty($user_input['captcha_response'])) { + $captcha_response = $user_input['captcha_response']; + } + + $captcha_type_challenge = $form_state->getTemporaryValue('recaptcha_v3_action_name'); + // Verify submitted reCAPTCHA v3 token. + $recaptcha_v3 = ReCaptchaV3Action::load($captcha_type_challenge) ?? ReCaptchaV3Action::create([ + 'id' => '', + 'label' => '', + 'threshold' => 1, + 'challenge' => 'default', + ]); + $verification_response = _recaptcha_v3_verify_captcha_response($recaptcha_v3, $captcha_response); + + if (!$verification_response['success']) { + // If we here, then token verification failed. + if ($verification_response['error-codes']) { + $errors = []; + foreach ($verification_response['error-codes'] as $code) { + $errors[] = recaptcha_v3_error_by_code($code); + } + $errors_string = implode(' ', $errors); + \Drupal::logger('recaptcha_v3')->error('Google reCAPTCHA v3 validation failed: @error', ['@error' => $errors_string]); + } + $form_state->clearErrors(); + $error_message = \Drupal::config('recaptcha_v3.settings') + ->get('error_message'); + if ($error_message) { + $form_state->setErrorByName('captcha_response', $error_message); + } + } + else { + $errors = $form_state->getErrors(); + // If captcha validated, then need to remove error related to the + // captcha_response element. Otherwise, for example, if fallback is + // captcha Math we will get error about exceeding input value length due to + // recaptcha v3 response is much longer than allowed for Math captcha. + if (isset($errors['captcha_response'])) { + $form_state->clearErrors(); + foreach ($errors as $name => $error) { + if ($name !== 'captcha_response') { + $form_state->setErrorByName($name, $error); + } + } + } + } + // Save captcha response in $form_state storage to prevent + // further validation requests. + $form_state->set('recaptcha_v3', $verification_response); + + // Need to cache form, because previous line does not make sense + // because form will not cached otherwise. Probably this is wrong because + // if form set for rebuilding it will not cached anyway. + // @todo need to check if need to move next line to the element preprocess callback. + $form_state->setCached(); + + return (bool) $verification_response['success']; +} + +/** + * AJAX callback; return captcha response. + * + * @param array $form + * An array of $form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The recaptcha v3 form state. + * + * @return \Drupal\Core\Ajax\AjaxResponse + * Return captcha response. + */ +function recaptcha_v3_ajax_callback(array $form, FormStateInterface $form_state) { + $response = new AjaxResponse(); + $selector = '#' . $form['#id'] . ' .captcha'; + $response->addCommand(new ReplaceCommand($selector, $form['captcha'])); + return $response; +} + +/** + * Verify captcha response. + * + * @param \Drupal\recaptcha_v3\ReCaptchaV3ActionInterface $recaptcha_v_3_action + * Fetch the recaptcha v3 action. + * @param string $captcha_response + * Captcha response token. + * + * @return array + * Return the response from google recaptcha api. + */ +function _recaptcha_v3_verify_captcha_response(ReCaptchaV3ActionInterface $recaptcha_v_3_action, $captcha_response) { + $request = Drupal::request(); + $config = Drupal::config('recaptcha_v3.settings'); + $recaptcha = new ReCaptcha($config->get('secret_key')); + + if ($config->get('verify_hostname')) { + $recaptcha->setExpectedHostname($request->getHost()); + } + + return $recaptcha->setExpectedAction($recaptcha_v_3_action->id()) + ->setScoreThreshold($recaptcha_v_3_action->getThreshold()) + ->verify($captcha_response, $request->getClientIp()) + ->toArray(); +} + +/** + * Return error by code from response. + * + * Error code reference, https://developers.google.com/recaptcha/docs/verify. + * + * @param string $code + * The error code in google api response. + * + * @return \Drupal\Core\StringTranslation\TranslatableMarkup + * Return an array of error code description. + */ +function recaptcha_v3_error_by_code($code) { + $error_codes = &drupal_static(__FUNCTION__); + if (!isset($error_codes)) { + $error_codes = [ + 'timeout-or-duplicate' => t('The response is no longer valid: either is too old or has been used previously.'), + 'bad-request' => t('The request is invalid or malformed.'), + 'missing-input-secret' => t('The secret parameter is missing.'), + 'invalid-input-secret' => t('The secret parameter is invalid or malformed.'), + '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.'), + 'challenge-timeout' => t('Challenge timeout.'), + 'connection-failed' => t('Could not connect to service.'), + 'invalid-input-response' => t('The response parameter is invalid or malformed.'), + 'missing-input-response' => t('The response parameter is missing.'), + 'hostname-mismatch' => t('Expected hostname did not match.'), + 'invalid-json' => t('The json response is invalid or malformed.'), + 'score-threshold-not-met' => t('Score threshold not met.'), + 'unknown-error' => t('Not a success, but no error codes received!'), + ]; + } + return $error_codes[$code] ?? $error_codes['unknown-error']; +} diff --git a/web/modules/recaptcha_v3/recaptcha_v3.routing.yml b/web/modules/recaptcha_v3/recaptcha_v3.routing.yml new file mode 100644 index 0000000000000000000000000000000000000000..2da7d25c298d3d24a27fb7e25b8476d2204513cf --- /dev/null +++ b/web/modules/recaptcha_v3/recaptcha_v3.routing.yml @@ -0,0 +1,9 @@ +recaptcha_v3.settings: + path: '/admin/config/people/captcha/recaptcha-v3' + defaults: + _form: '\Drupal\recaptcha_v3\Form\ReCaptchaV3SettingsForm' + _title: 'reCAPTCHA v3 settings' + requirements: + _permission: 'administer CAPTCHA settings' + options: + _admin_route: TRUE diff --git a/web/modules/recaptcha_v3/src/Entity/ReCaptchaV3Action.php b/web/modules/recaptcha_v3/src/Entity/ReCaptchaV3Action.php new file mode 100644 index 0000000000000000000000000000000000000000..d0d6614db2acf817221a9c34721c1eb2027e7c02 --- /dev/null +++ b/web/modules/recaptcha_v3/src/Entity/ReCaptchaV3Action.php @@ -0,0 +1,122 @@ +<?php + +namespace Drupal\recaptcha_v3\Entity; + +use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\recaptcha_v3\ReCaptchaV3ActionInterface; + +/** + * Defines the reCAPTCHA v3 action entity. + * + * @ConfigEntityType( + * id = "recaptcha_v3_action", + * label = @Translation("reCAPTCHA v3 action"), + * label_collection = @Translation("reCAPTCHA v3 actions"), + * label_singular = @Translation("reCAPTCHA v3 action"), + * label_plural = @Translation("reCAPTCHA v3 actions"), + * label_count = @PluralTranslation( + * singular = "@count reCAPTCHA v3 action", + * plural = "@count reCAPTCHA v3 actions", + * ), + * handlers = { + * "view_builder" = "Drupal\Core\Entity\EntityViewBuilder", + * "list_builder" = "Drupal\recaptcha_v3\ReCaptchaV3ActionListBuilder", + * "form" = { + * "add" = "Drupal\recaptcha_v3\Form\ReCaptchaV3ActionForm", + * "edit" = "Drupal\recaptcha_v3\Form\ReCaptchaV3ActionForm", + * "delete" = "Drupal\recaptcha_v3\Form\ReCaptchaV3ActionDeleteForm" + * }, + * "route_provider" = { + * "default" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider", + * }, + * }, + * config_prefix = "recaptcha_v3_action", + * admin_permission = "administer CAPTCHA settings", + * list_cache_tags = { + * "rendered" + * }, + * entity_keys = { + * "id" = "id", + * "label" = "label", + * "uuid" = "uuid", + * }, + * config_export = { + * "id", + * "label", + * "threshold", + * "challenge", + * }, + * links = { + * "add-form" = "/admin/config/people/captcha/recaptcha-v3-actions/add", + * "edit-form" = "/admin/config/people/captcha/recaptcha-v3-actions/{recaptcha_v3_action}", + * "delete-form" = "/admin/config/people/captcha/recaptcha-v3-actions/{recaptcha_v3_action}/delete", + * "collection" = "/admin/config/people/captcha/recaptcha-v3-actions" + * } + * ) + */ +class ReCaptchaV3Action extends ConfigEntityBase implements ReCaptchaV3ActionInterface { + + /** + * The reCAPTCHA v3 action ID. + * + * @var string + */ + protected $id; + + /** + * The reCAPTCHA v3 action label. + * + * @var string + */ + protected $label; + + /** + * The reCAPTCHA v3 action threshold. + * + * @var float + */ + protected $threshold = 1; + + /** + * The reCAPTCHA v3 action fallback challenge. + * + * @var string + */ + protected $challenge = 'default'; + + /** + * {@inheritdoc} + */ + public function setLabel(string $label) { + $this->label = $label; + } + + /** + * {@inheritdoc} + */ + public function getThreshold(): float { + return $this->threshold; + } + + /** + * {@inheritdoc} + */ + public function setThreshold(float $threshold) { + $this->threshold = $threshold; + } + + /** + * {@inheritdoc} + */ + public function getChallenge(): string { + return $this->challenge; + } + + /** + * {@inheritdoc} + */ + public function setChallenge(string $challenge) { + $this->challenge = $challenge; + } + +} diff --git a/web/modules/recaptcha_v3/src/Form/ReCaptchaV3ActionDeleteForm.php b/web/modules/recaptcha_v3/src/Form/ReCaptchaV3ActionDeleteForm.php new file mode 100644 index 0000000000000000000000000000000000000000..8b6c7a0a048c926a6c2a08134ef802a7b9ad5f69 --- /dev/null +++ b/web/modules/recaptcha_v3/src/Form/ReCaptchaV3ActionDeleteForm.php @@ -0,0 +1,42 @@ +<?php + +namespace Drupal\recaptcha_v3\Form; + +use Drupal\Core\Entity\EntityDeleteForm; +use Drupal\Core\Form\FormStateInterface; + +/** + * Provides a confirmation form for deleting reCAPTCHA v3 action entities. + * + * @internal + */ +class ReCaptchaV3ActionDeleteForm extends EntityDeleteForm { + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + + $action = $this->entityTypeManager->getStorage('captcha_point')->getQuery() + ->condition('captchaType', 'recaptcha_v3/' . $this->entity->id()) + ->execute(); + if (!empty($action)) { + $caption = '<p>' . $this->formatPlural( + count($action), + '%label is used by 1 captcha point form on your site. You can not remove %label until you have removed it from %formId captcha points form.', + '%label is used by @count captcha point forms on your site. You may not remove %label until you have removed %label from %formId.', + [ + '%label' => $this->entity->label(), + '%formId' => implode(", ", $action), + ] + ) . '</p>'; + $form['description'] = ['#markup' => $caption]; + return $form; + } + else { + return parent::buildForm($form, $form_state); + } + // @todo needs to do same as above in case of recaptcha v3 action being used in webform. + } + +} diff --git a/web/modules/recaptcha_v3/src/Form/ReCaptchaV3ActionForm.php b/web/modules/recaptcha_v3/src/Form/ReCaptchaV3ActionForm.php new file mode 100644 index 0000000000000000000000000000000000000000..5bba600e2b3d901d917826a1e5316fe02870e89b --- /dev/null +++ b/web/modules/recaptcha_v3/src/Form/ReCaptchaV3ActionForm.php @@ -0,0 +1,124 @@ +<?php + +namespace Drupal\recaptcha_v3\Form; + +use Drupal\captcha\Service\CaptchaService; +use Drupal\Core\Entity\EntityForm; +use Drupal\Core\Form\FormStateInterface; +use Drupal\recaptcha_v3\Entity\ReCaptchaV3Action; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Form controller for the recaptcha_v3_action entity edit forms. + * + * @internal + */ +class ReCaptchaV3ActionForm extends EntityForm { + + /** + * The CAPTCHA helper service. + * + * @var \Drupal\captcha\Service\CaptchaService + */ + protected $captchaService; + + /** + * Constructs a ReCaptchaV3ActionForm. + * + * @param \Drupal\captcha\Service\CaptchaService $captcha_service + * Captcha service. + */ + public function __construct(CaptchaService $captcha_service) { + $this->captchaService = $captcha_service; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('captcha.helper')); + } + + /** + * {@inheritdoc} + */ + public function form(array $form, FormStateInterface $form_state) { + $form = parent::form($form, $form_state); + $recaptcha_v3_action = $this->entity; + $form['label'] = [ + '#type' => 'textfield', + '#title' => $this->t('Label'), + '#maxlength' => 255, + '#default_value' => $recaptcha_v3_action->label(), + '#description' => $this->t('Label for the reCAPTCHA v3 action.'), + '#required' => TRUE, + ]; + + $form['id'] = [ + '#type' => 'machine_name', + '#default_value' => $recaptcha_v3_action->id(), + '#machine_name' => [ + 'exists' => [ReCaptchaV3Action::class, 'load'], + ], + '#disabled' => !$recaptcha_v3_action->isNew(), + ]; + + $form['threshold'] = [ + '#type' => 'number', + '#title' => $this->t('Threshold'), + '#min' => 0, + '#max' => 1, + '#step' => 0.1, + '#default_value' => $recaptcha_v3_action->getThreshold(), + ]; + + // @todo the same code lines using in several other places + // need to refactor this. + // Maybe create method in recaptcha v3 action storage? + $challenges = $this->captchaService->getAvailableChallengeTypes(FALSE); + // Remove recaptcha v3 challenges from the list of available + // fallback challenges. + $challenges = array_filter($challenges, static function ($captcha_type) { + return !(strpos($captcha_type, 'recaptcha_v3') === 0); + }, ARRAY_FILTER_USE_KEY); + $challenges = ['default' => $this->t('Default fallback challenge')] + $challenges; + + $form['challenge'] = [ + '#type' => 'select', + '#title' => $this->t('Fallback challenge'), + '#description' => $this->t('Select the fallback challenge on reCAPTCHA v3 user validation fail.'), + '#options' => $challenges, + '#default_value' => $recaptcha_v3_action->getChallenge(), + '#empty_option' => $this->t('- None -'), + '#empty_value' => '', + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function save(array $form, FormStateInterface $form_state) { + $label = $this->entity->label(); + $saved_state = parent::save($form, $form_state); + switch ($saved_state) { + case SAVED_NEW: + $this->messenger() + ->addStatus($this->t('Created the %label reCAPTCHA v3 action.', ['%label' => $label])); + $this->getLogger('recaptcha_v3') + ->info('Created the %label reCAPTCHA v3 action.', ['%label' => $label]); + break; + + default: + $this->messenger() + ->addStatus($this->t('Saved the %label reCAPTCHA v3 action.', ['%label' => $label])); + $this->getLogger('recaptcha_v3') + ->info('Saved the %label reCAPTCHA v3 action.', ['%label' => $label]); + } + + $form_state->setRedirectUrl($this->entity->toUrl('collection')); + return $saved_state; + } + +} diff --git a/web/modules/recaptcha_v3/src/Form/ReCaptchaV3SettingsForm.php b/web/modules/recaptcha_v3/src/Form/ReCaptchaV3SettingsForm.php new file mode 100644 index 0000000000000000000000000000000000000000..450e4df4c9bb69fde0782f879762bd8c2e03a955 --- /dev/null +++ b/web/modules/recaptcha_v3/src/Form/ReCaptchaV3SettingsForm.php @@ -0,0 +1,173 @@ +<?php + +namespace Drupal\recaptcha_v3\Form; + +use Drupal\captcha\Service\CaptchaService; +use Drupal\Core\Asset\LibraryDiscoveryInterface; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Form\ConfigFormBase; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Render\ElementInfoManager; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Configure the google reCAPTCHA v3 api and fallback challenge. + */ +class ReCaptchaV3SettingsForm extends ConfigFormBase { + + /** + * The library discovery service. + * + * @var \Drupal\Core\Asset\LibraryDiscoveryInterface + */ + protected $libraryDiscovery; + + /** + * The element info manager. + * + * @var \Drupal\Core\Render\ElementInfoManager + */ + protected $elementInfoManager; + + /** + * The CAPTCHA helper service. + * + * @var \Drupal\captcha\Service\CaptchaService + */ + protected $captchaService; + + /** + * ReCaptchaV3SettingsForm constructor. + * + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * Config factory service. + * @param \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery + * Library discovery service. + * @param \Drupal\Core\Render\ElementInfoManager $element_info_manager + * Element info manager service. + * @param \Drupal\captcha\Service\CaptchaService $captcha_service + * Captcha service. + */ + public function __construct(ConfigFactoryInterface $config_factory, LibraryDiscoveryInterface $library_discovery, ElementInfoManager $element_info_manager, CaptchaService $captcha_service) { + parent::__construct($config_factory); + $this->libraryDiscovery = $library_discovery; + $this->elementInfoManager = $element_info_manager; + $this->captchaService = $captcha_service; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('library.discovery'), + $container->get('plugin.manager.element_info'), + $container->get('captcha.helper') + ); + } + + /** + * {@inheritdoc} + */ + protected function getEditableConfigNames() { + return [ + 'recaptcha_v3.settings', + ]; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'recaptcha_v3_settings'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + + $config = $this->config('recaptcha_v3.settings'); + + $form['site_key'] = [ + '#type' => 'textfield', + '#title' => $this->t('Site key'), + '#default_value' => $config->get('site_key'), + '#maxlength' => 40, + '#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']), + '#required' => TRUE, + ]; + $form['secret_key'] = [ + '#type' => 'textfield', + '#title' => $this->t('Secret key'), + '#default_value' => $config->get('secret_key'), + '#maxlength' => 40, + '#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']), + '#required' => TRUE, + ]; + $form['verify_hostname'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Local domain name validation'), + '#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.'), + ]; + + $challenges = $this->captchaService->getAvailableChallengeTypes(FALSE); + // Remove recaptcha v3 challenges from the list of available + // fallback challenges. + $challenges = array_filter($challenges, static function ($captcha_type) { + return !(strpos($captcha_type, 'recaptcha_v3') === 0); + }, ARRAY_FILTER_USE_KEY); + + $form['default_challenge'] = [ + '#type' => 'select', + '#title' => $this->t('Default fallback challenge type'), + '#description' => $this->t('Select the default fallback challenge type on verification fail.'), + '#options' => $challenges, + '#default_value' => $config->get('default_challenge'), + '#empty_option' => $this->t('- None -'), + '#empty_value' => '', + ]; + + $form['error_message'] = [ + '#type' => 'textfield', + '#size' => 128, + '#title' => $this->t('Error message'), + '#description' => $this->t('This message will be displayed to user in case of failed recaptcha v3 verification.'), + '#default_value' => $config->get('error_message'), + ]; + + $form['cacheable'] = [ + '#type' => 'checkbox', + '#title' => $this->t('Cacheable'), + '#description' => $this->t('Make captcha cacheble: can lead to some validation errors like "unknown CAPTCHA session ID".'), + '#default_value' => $config->get('cacheable'), + ]; + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $values = $form_state->getValues(); + $config = $this->config('recaptcha_v3.settings'); + // If site key have been changed, + // then need to rebuild site libraries and elements. + if ($config->get('site_key') !== $values['site_key']) { + $this->libraryDiscovery->clearCachedDefinitions(); + $this->elementInfoManager->clearCachedDefinitions(); + } + $this->config('recaptcha_v3.settings') + ->set('site_key', $values['site_key']) + ->set('secret_key', $values['secret_key']) + ->set('verify_hostname', $values['verify_hostname']) + ->set('default_challenge', $values['default_challenge']) + ->set('error_message', $values['error_message']) + ->save(); + + parent::submitForm($form, $form_state); + } + +} diff --git a/web/modules/recaptcha_v3/src/ReCaptchaV3ActionInterface.php b/web/modules/recaptcha_v3/src/ReCaptchaV3ActionInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..99c7ee4b450ba3a7b9767dbcca2f3b057ae13673 --- /dev/null +++ b/web/modules/recaptcha_v3/src/ReCaptchaV3ActionInterface.php @@ -0,0 +1,52 @@ +<?php + +namespace Drupal\recaptcha_v3; + +use Drupal\Core\Config\Entity\ConfigEntityInterface; + +/** + * Provides an interface defining a recaptcha v3 entity.. + */ +interface ReCaptchaV3ActionInterface extends ConfigEntityInterface { + + /** + * Setter for label. + * + * @param string $label + * Label of action. + */ + public function setLabel(string $label); + + /** + * Getter for threshold. + * + * @return float + * Get threshold value. + */ + public function getThreshold(): float; + + /** + * Setter for threshold. + * + * @param float $threshold + * Set threshold value. + */ + public function setThreshold(float $threshold); + + /** + * Getter for challenge. + * + * @return string + * Challenge type. + */ + public function getChallenge(): string; + + /** + * Setter for challenge. + * + * @param string $challenge + * Set challenge type. + */ + public function setChallenge(string $challenge); + +} diff --git a/web/modules/recaptcha_v3/src/ReCaptchaV3ActionListBuilder.php b/web/modules/recaptcha_v3/src/ReCaptchaV3ActionListBuilder.php new file mode 100644 index 0000000000000000000000000000000000000000..f9c2e27ba74f3203217d8a7d6b511c3dfa42be63 --- /dev/null +++ b/web/modules/recaptcha_v3/src/ReCaptchaV3ActionListBuilder.php @@ -0,0 +1,63 @@ +<?php + +namespace Drupal\recaptcha_v3; + +use Drupal\Core\Config\Entity\ConfigEntityListBuilder; +use Drupal\Core\Entity\EntityInterface; + +/** + * Provides a listing of reCAPTCHA v3 action entities. + */ +class ReCaptchaV3ActionListBuilder extends ConfigEntityListBuilder { + + /** + * Recaptcha v3 challenge types. + * + * @var array + * An array of recaptcha v3 challenge types. + */ + protected $challengeTypes; + + /** + * {@inheritdoc} + */ + public function buildHeader() { + $header['label'] = $this->t('Label'); + $header['id'] = $this->t('Action'); + $header['threshold'] = $this->t('Threshold'); + $header['challenge'] = $this->t('Fail challenge'); + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $entity) { + /** @var \Drupal\recaptcha_v3\ReCaptchaV3ActionInterface $entity */ + $row['label'] = $entity->label(); + $row['id'] = $entity->id(); + $row['threshold'] = $entity->getThreshold(); + $challenge_type = $entity->getChallenge(); + $row['challenge'] = $this->getCaptchaChallengeTypes()[$challenge_type] ?? $this->t('Not defined'); + return $row + parent::buildRow($entity); + } + + /** + * Get reCaptcha v3 challenge types. + * + * @return array + * All reCaptcha v3 challenge types. + */ + protected function getCaptchaChallengeTypes() { + if ($this->challengeTypes === NULL) { + $this->challengeTypes = \Drupal::service('captcha.helper')->getAvailableChallengeTypes(FALSE); + $this->challengeTypes = array_filter($this->challengeTypes, static function ($captcha_type) { + return !(strpos($captcha_type, 'recaptcha_v3') === 0); + }, ARRAY_FILTER_USE_KEY); + $default = \Drupal::config('recaptcha_v3.settings')->get('default_challenge'); + $this->challengeTypes['default'] = $this->challengeTypes[$default] ?? $this->t('Default'); + } + return $this->challengeTypes; + } + +} diff --git a/web/modules/recaptcha_v3/tests/src/Functional/ReCaptchaActionListBuilderTest.php b/web/modules/recaptcha_v3/tests/src/Functional/ReCaptchaActionListBuilderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3815f87cf107364511a29d169685a572cb2c0bc6 --- /dev/null +++ b/web/modules/recaptcha_v3/tests/src/Functional/ReCaptchaActionListBuilderTest.php @@ -0,0 +1,67 @@ +<?php + +namespace Drupal\Tests\recaptcha_v3\Functional; + +use Drupal\Core\Url; +use Drupal\Tests\BrowserTestBase; + +/** + * Class ReCaptchaActionListBuilderTest. + * + * @package Drupal\Tests\recaptcha_v3\Functional + * + * @group recaptcha_v3 + */ +class ReCaptchaActionListBuilderTest extends BrowserTestBase { + + protected $defaultTheme = 'stark'; + + /** + * Modules to enable. + * + * @var array + */ + protected static $modules = [ + 'captcha', + 'recaptcha_v3', + ]; + + /** + * Test case for the recaptcha action list builder. + */ + public function testListBuilder() { + $add_form = Url::fromRoute('entity.recaptcha_v3_action.add_form'); + $collection = Url::fromRoute('entity.recaptcha_v3_action.collection'); + + $assert = $this->assertSession(); + + // Ensure anonymous access is denied to the add form. + $this->drupalGet($add_form); + $assert->statusCodeEquals(403); + + // Ensure anonymous access is denied to the collection form. + $this->drupalGet($collection); + $assert->statusCodeEquals(403); + + // Sign in as a captcha administrator. + $this->drupalLogIn($this->createUser(['administer CAPTCHA settings'])); + + // Add an action. + $this->drupalPostForm( + $add_form, [ + 'label' => 'Test action', + 'id' => 'test_action', + 'threshold' => '.5', + 'challenge' => 'default', + ], + 'Save'); + + // Check that the collection contains the new action. + $this->drupalGet($collection); + $assert->pageTextContains('Test action'); + $assert->pageTextContains('test_action'); + $assert->pageTextContains('.5'); + $assert->pageTextContains('Default'); + } + +}