PluginProbe
User Submitted Posts – Enable Users to Submit Posts from the Front End / 20260916
User Submitted Posts – Enable Users to Submit Posts from the Front End v20260916
20260916 20260810 20260608 20230806 20230809 20230811 20230901 20230902 20230914 20231102 20240319 20240516 20240703 20241026 20250327 20250329 20251121 20251210 20260110 20260113 20260207 20260217 20260407 20260422 trunk All 59 releases
user-submitted-posts / recaptcha / ReCaptcha / ReCaptcha.php

ReCaptcha.php in User Submitted Posts – Enable Users to Submit Posts from the Front End 20260916, at recaptcha/ReCaptcha/ReCaptcha.php

270 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * This is a PHP library that handles calling reCAPTCHA.
4 *
5 * BSD 3-Clause License
6 * @copyright (c) 2019, Google Inc.
7 * @link https://www.google.com/recaptcha
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are met:
12 * 1. Redistributions of source code must retain the above copyright notice, this
13 * list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the copyright holder nor the names of its
20 * contributors may be used to endorse or promote products derived from
21 * this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 namespace ReCaptcha;
36
37 /**
38 * reCAPTCHA client.
39 */
40 class ReCaptcha
41 {
42 /**
43 * Version of this client library.
44 * @const string
45 */
46 const VERSION = 'php_1.2.3';
47
48 /**
49 * URL for reCAPTCHA siteverify API
50 * @const string
51 */
52 const SITE_VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
53
54 /**
55 * Invalid JSON received
56 * @const string
57 */
58 const E_INVALID_JSON = 'invalid-json';
59
60 /**
61 * Could not connect to service
62 * @const string
63 */
64 const E_CONNECTION_FAILED = 'connection-failed';
65
66 /**
67 * Did not receive a 200 from the service
68 * @const string
69 */
70 const E_BAD_RESPONSE = 'bad-response';
71
72 /**
73 * Not a success, but no error codes received!
74 * @const string
75 */
76 const E_UNKNOWN_ERROR = 'unknown-error';
77
78 /**
79 * ReCAPTCHA response not provided
80 * @const string
81 */
82 const E_MISSING_INPUT_RESPONSE = 'missing-input-response';
83
84 /**
85 * Expected hostname did not match
86 * @const string
87 */
88 const E_HOSTNAME_MISMATCH = 'hostname-mismatch';
89
90 /**
91 * Expected APK package name did not match
92 * @const string
93 */
94 const E_APK_PACKAGE_NAME_MISMATCH = 'apk_package_name-mismatch';
95
96 /**
97 * Expected action did not match
98 * @const string
99 */
100 const E_ACTION_MISMATCH = 'action-mismatch';
101
102 /**
103 * Score threshold not met
104 * @const string
105 */
106 const E_SCORE_THRESHOLD_NOT_MET = 'score-threshold-not-met';
107
108 /**
109 * Challenge timeout
110 * @const string
111 */
112 const E_CHALLENGE_TIMEOUT = 'challenge-timeout';
113
114 /**
115 * Shared secret for the site.
116 * @var string
117 */
118 private $secret;
119
120 /**
121 * Method used to communicate with service. Defaults to POST request.
122 * @var RequestMethod
123 */
124 private $requestMethod;
125
126 /**
127 * Create a configured instance to use the reCAPTCHA service.
128 *
129 * @param string $secret The shared key between your site and reCAPTCHA.
130 * @param RequestMethod $requestMethod method used to send the request. Defaults to POST.
131 * @throws \RuntimeException if $secret is invalid
132 */
133 public function __construct($secret, RequestMethod $requestMethod = null)
134 {
135 if (empty($secret)) {
136 throw new \RuntimeException('No secret provided');
137 }
138
139 if (!is_string($secret)) {
140 throw new \RuntimeException('The provided secret must be a string');
141 }
142
143 $this->secret = $secret;
144 $this->requestMethod = (is_null($requestMethod)) ? new RequestMethod\Post() : $requestMethod;
145 }
146
147 /**
148 * Calls the reCAPTCHA siteverify API to verify whether the user passes
149 * CAPTCHA test and additionally runs any specified additional checks
150 *
151 * @param string $response The user response token provided by reCAPTCHA, verifying the user on your site.
152 * @param string $remoteIp The end user's IP address.
153 * @return Response Response from the service.
154 */
155 public function verify($response, $remoteIp = null)
156 {
157 // Discard empty solution submissions
158 if (empty($response)) {
159 $recaptchaResponse = new Response(false, array(self::E_MISSING_INPUT_RESPONSE));
160 return $recaptchaResponse;
161 }
162
163 $params = new RequestParameters($this->secret, $response, $remoteIp, self::VERSION);
164 $rawResponse = $this->requestMethod->submit($params);
165 $initialResponse = Response::fromJson($rawResponse);
166 $validationErrors = array();
167
168 if (isset($this->hostname) && strcasecmp($this->hostname, $initialResponse->getHostname()) !== 0) {
169 $validationErrors[] = self::E_HOSTNAME_MISMATCH;
170 }
171
172 if (isset($this->apkPackageName) && strcasecmp($this->apkPackageName, $initialResponse->getApkPackageName()) !== 0) {
173 $validationErrors[] = self::E_APK_PACKAGE_NAME_MISMATCH;
174 }
175
176 if (isset($this->action) && strcasecmp($this->action, $initialResponse->getAction()) !== 0) {
177 $validationErrors[] = self::E_ACTION_MISMATCH;
178 }
179
180 if (isset($this->threshold) && $this->threshold > $initialResponse->getScore()) {
181 $validationErrors[] = self::E_SCORE_THRESHOLD_NOT_MET;
182 }
183
184 if (isset($this->timeoutSeconds)) {
185 $challengeTs = strtotime($initialResponse->getChallengeTs());
186
187 if ($challengeTs > 0 && time() - $challengeTs > $this->timeoutSeconds) {
188 $validationErrors[] = self::E_CHALLENGE_TIMEOUT;
189 }
190 }
191
192 if (empty($validationErrors)) {
193 return $initialResponse;
194 }
195
196 return new Response(
197 false,
198 array_merge($initialResponse->getErrorCodes(), $validationErrors),
199 $initialResponse->getHostname(),
200 $initialResponse->getChallengeTs(),
201 $initialResponse->getApkPackageName(),
202 $initialResponse->getScore(),
203 $initialResponse->getAction()
204 );
205 }
206
207 /**
208 * Provide a hostname to match against in verify()
209 * This should be without a protocol or trailing slash, e.g. www.google.com
210 *
211 * @param string $hostname Expected hostname
212 * @return ReCaptcha Current instance for fluent interface
213 */
214 public function setExpectedHostname($hostname)
215 {
216 $this->hostname = $hostname;
217 return $this;
218 }
219
220 /**
221 * Provide an APK package name to match against in verify()
222 *
223 * @param string $apkPackageName Expected APK package name
224 * @return ReCaptcha Current instance for fluent interface
225 */
226 public function setExpectedApkPackageName($apkPackageName)
227 {
228 $this->apkPackageName = $apkPackageName;
229 return $this;
230 }
231
232 /**
233 * Provide an action to match against in verify()
234 * This should be set per page.
235 *
236 * @param string $action Expected action
237 * @return ReCaptcha Current instance for fluent interface
238 */
239 public function setExpectedAction($action)
240 {
241 $this->action = $action;
242 return $this;
243 }
244
245 /**
246 * Provide a threshold to meet or exceed in verify()
247 * Threshold should be a float between 0 and 1 which will be tested as response >= threshold.
248 *
249 * @param float $threshold Expected threshold
250 * @return ReCaptcha Current instance for fluent interface
251 */
252 public function setScoreThreshold($threshold)
253 {
254 $this->threshold = floatval($threshold);
255 return $this;
256 }
257
258 /**
259 * Provide a timeout in seconds to test against the challenge timestamp in verify()
260 *
261 * @param int $timeoutSeconds Expected hostname
262 * @return ReCaptcha Current instance for fluent interface
263 */
264 public function setChallengeTimeout($timeoutSeconds)
265 {
266 $this->timeoutSeconds = $timeoutSeconds;
267 return $this;
268 }
269 }
270