Completed
Branch BUG/11475/decode-site-title-fo... (bbd86e)
by
unknown
13:39 queued 25s
created

InvisibleRecaptcha::getInputHtml()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace EventEspresso\caffeinated\modules\recaptcha_invisible;
4
5
use DomainException;
6
use EE_Error;
7
use EE_Form_Section_Proper;
8
use EE_Invisible_Recaptcha_Input;
9
use EE_Registration_Config;
10
use EE_Request;
11
use EE_Session;
12
use EventEspresso\core\exceptions\InvalidDataTypeException;
13
use EventEspresso\core\exceptions\InvalidInterfaceException;
14
use InvalidArgumentException;
15
use RuntimeException;
16
use WP_Error;
17
18
defined('EVENT_ESPRESSO_VERSION') || exit;
19
20
21
22
/**
23
 * Class ProcessInvisibleRecaptcha
24
 * Provides methods for integrating Google's Invisible reCAPTCHA into EE Forms
25
 * and for verifying the 'g-recaptcha-response' response token
26
 *
27
 * @package EventEspresso\caffeinated\modules\recaptcha_invisible
28
 * @author  Brent Christensen
29
 * @since   $VID:$
30
 */
31
class InvisibleRecaptcha
32
{
33
34
    const URL_GOOGLE_RECAPTCHA_API          = 'https://www.google.com/recaptcha/api/siteverify';
35
36
    const SESSION_DATA_KEY_RECAPTCHA_PASSED = 'recaptcha_passed';
37
38
    /**
39
     * @var EE_Registration_Config $config
40
     */
41
    private $config;
42
43
    /**
44
     * @var EE_Session $session
45
     */
46
    private $session;
47
48
    /**
49
     * @var boolean $recaptcha_passed
50
     */
51
    private $recaptcha_passed;
52
53
54
    /**
55
     * InvisibleRecaptcha constructor.
56
     *
57
     * @param EE_Registration_Config $registration_config
58
     * @param EE_Session             $session
59
     */
60
    public function __construct(EE_Registration_Config $registration_config, EE_Session $session)
61
    {
62
        $this->config = $registration_config;
63
        $this->session = $session;
64
    }
65
66
67
    /**
68
     * @return boolean
69
     */
70
    public function useInvisibleRecaptcha()
71
    {
72
        return $this->config->use_captcha && $this->config->recaptcha_theme === 'invisible';
73
    }
74
75
76
    /**
77
     * @param array $input_settings
78
     * @return EE_Invisible_Recaptcha_Input
79
     * @throws InvalidDataTypeException
80
     * @throws InvalidInterfaceException
81
     * @throws InvalidArgumentException
82
     * @throws DomainException
83
     */
84
    public function getInput(array $input_settings = array())
85
    {
86
        return new EE_Invisible_Recaptcha_Input(
87
            $input_settings,
88
            $this->config
89
        );
90
    }
91
92
93
    /**
94
     * @param array $input_settings
95
     * @return string
96
     * @throws EE_Error
97
     * @throws InvalidDataTypeException
98
     * @throws InvalidInterfaceException
99
     * @throws InvalidArgumentException
100
     * @throws DomainException
101
     */
102
    public function getInputHtml(array $input_settings = array())
103
    {
104
        return $this->getInput($input_settings)->get_html_for_input();
105
    }
106
107
108
    /**
109
     * @param EE_Form_Section_Proper $form
110
     * @param array                  $input_settings
111
     * @throws EE_Error
112
     * @throws InvalidArgumentException
113
     * @throws InvalidDataTypeException
114
     * @throws InvalidInterfaceException
115
     * @throws DomainException
116
     */
117
    public function addToFormSection(EE_Form_Section_Proper $form, array $input_settings = array())
118
    {
119
        $form->add_subsections(
120
            array(
121
                'espresso_recaptcha' => $this->getInput($input_settings),
122
            ),
123
            null,
124
            false
125
        );
126
    }
127
128
129
    /**
130
     * @param EE_Request $request
131
     * @return boolean
132
     * @throws RuntimeException
133
     */
134
    public function verifyToken(EE_Request $request)
135
    {
136
        static $previous_recaptcha_response = array();
137
        $grecaptcha_response = $request->get('g-recaptcha-response');
0 ignored issues
show
Deprecated Code introduced by
The method EE_Request::get() has been deprecated with message: 4.9.53

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
138
        // if this token has already been verified, then return previous response
139
        if (isset($previous_recaptcha_response[ $grecaptcha_response ])) {
140
            return $previous_recaptcha_response[ $grecaptcha_response ];
141
        }
142
        // will update to true if everything passes
143
        $previous_recaptcha_response[ $grecaptcha_response ] = false;
144
        $response                                            = wp_safe_remote_post(
145
            InvisibleRecaptcha::URL_GOOGLE_RECAPTCHA_API,
146
            array(
147
                'body' => array(
148
                    'secret'   => $this->config->recaptcha_privatekey,
149
                    'response' => $grecaptcha_response,
150
                    'remoteip' => $request->ip_address(),
0 ignored issues
show
Deprecated Code introduced by
The method EE_Request::ip_address() has been deprecated with message: 4.9.53

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
151
                ),
152
            )
153
        );
154
        if ($response instanceof WP_Error) {
0 ignored issues
show
Bug introduced by
The class WP_Error does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
155
            $this->generateError($response->get_error_messages());
156
            return false;
157
        }
158
        $results = json_decode(wp_remote_retrieve_body($response), true);
159
        if (filter_var($results['success'], FILTER_VALIDATE_BOOLEAN) !== true) {
160
            $errors   = array_map(
161
                array($this, 'getErrorCode'),
162
                $results['error-codes']
163
            );
164
            if(isset($results['challenge_ts'])) {
165
                $errors[] = 'challenge timestamp: ' . $results['challenge_ts'] . '.';
166
            }
167
            $this->generateError(implode(' ', $errors));
168
        }
169
        $previous_recaptcha_response[ $grecaptcha_response ] = true;
170
        add_action('shutdown', array($this, 'setSessionData'));
171
        return true;
172
    }
173
174
175
    /**
176
     * @param string $error_response
177
     * @return void
178
     * @throws RuntimeException
179
     */
180
    public function generateError($error_response = '')
181
    {
182
        throw new RuntimeException(
183
            sprintf(
184
                esc_html__(
185
                    'We\'re sorry but an attempt to verify the form\'s reCAPTCHA has failed. %1$s %2$s Please try again.',
186
                    'event_espresso'
187
                ),
188
                '<br />',
189
                current_user_can('manage_options') ? $error_response : ''
190
            )
191
        );
192
    }
193
194
195
    /**
196
     * @param string $error_code
197
     * @return string
198
     */
199
    public function getErrorCode(&$error_code)
200
    {
201
        $error_codes = array(
202
            'missing-input-secret'   => 'The secret parameter is missing.',
203
            'invalid-input-secret'   => 'The secret parameter is invalid or malformed.',
204
            'missing-input-response' => 'The response parameter is missing.',
205
            'invalid-input-response' => 'The response parameter is invalid or malformed.',
206
            'bad-request'            => 'The request is invalid or malformed.',
207
            'timeout-or-duplicate'   => 'The request took too long to be sent or was a duplicate of a previous request.',
208
        );
209
        return isset($error_codes[ $error_code ]) ? $error_codes[ $error_code ] : '';
210
    }
211
212
213
    /**
214
     * @return array
215
     * @throws InvalidInterfaceException
216
     * @throws InvalidDataTypeException
217
     * @throws InvalidArgumentException
218
     */
219
    public function getLocalizedVars()
220
    {
221
        return (array) apply_filters(
222
            'FHEE__EventEspresso_caffeinated_modules_recaptcha_invisible_InvisibleRecaptcha__getLocalizedVars__localized_vars',
223
            array(
224
                'siteKey'          => $this->config->recaptcha_publickey,
225
                'recaptcha_passed' => $this->recaptchaPassed(),
226
                'wp_debug'         => WP_DEBUG,
227
                'disable_submit'   => defined('EE_EVENT_QUEUE_BASE_URL'),
228
            )
229
        );
230
    }
231
232
233
    /**
234
     * @return boolean
235
     * @throws InvalidInterfaceException
236
     * @throws InvalidDataTypeException
237
     * @throws InvalidArgumentException
238
     */
239
    public function recaptchaPassed()
240
    {
241
        if ($this->recaptcha_passed !== null) {
242
            return $this->recaptcha_passed;
243
        }
244
        // logged in means you have already passed a turing test of sorts
245
        if ($this->useInvisibleRecaptcha() === false || is_user_logged_in()) {
246
            $this->recaptcha_passed = true;
247
            return $this->recaptcha_passed;
248
        }
249
        // was test already passed?
250
        $this->recaptcha_passed = filter_var(
251
            $this->session->get_session_data(
252
                InvisibleRecaptcha::SESSION_DATA_KEY_RECAPTCHA_PASSED
253
            ),
254
            FILTER_VALIDATE_BOOLEAN
255
        );
256
        return $this->recaptcha_passed;
257
    }
258
259
260
    /**
261
     * @throws InvalidArgumentException
262
     * @throws InvalidDataTypeException
263
     * @throws InvalidInterfaceException
264
     */
265
    public function setSessionData()
266
    {
267
        $this->session->set_session_data(
268
            array(InvisibleRecaptcha::SESSION_DATA_KEY_RECAPTCHA_PASSED => true)
269
        );
270
    }
271
}
272