PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.3
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.3
3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.3.1 All 196 releases
convertkit / includes / class-convertkit-cloudflare-turnstile.php

class-convertkit-cloudflare-turnstile.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.4.3, at includes/class-convertkit-cloudflare-turnstile.php

204 lines 5.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ConvertKit Cloudflare Turnstile class.
4 *
5 * @package ConvertKit
6 * @author ConvertKit
7 */
8
9 /**
10 * Handles Cloudflare Turnstile verification.
11 *
12 * @since 3.3.7
13 */
14 class ConvertKit_Cloudflare_Turnstile {
15
16 /**
17 * The endpoint used to validate Turnstile tokens server-side.
18 *
19 * @since 3.3.7
20 *
21 * @var string
22 */
23 const SITEVERIFY_URL = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
24
25 /**
26 * The URL of the Turnstile client-side script.
27 *
28 * @since 3.3.7
29 *
30 * @var string
31 */
32 const CLIENT_SCRIPT_URL = 'https://challenges.cloudflare.com/turnstile/v0/api.js';
33
34 /**
35 * Holds the settings class.
36 *
37 * @since 3.3.7
38 *
39 * @var bool|ConvertKit_Settings
40 */
41 private $settings = false;
42
43 /**
44 * Constructor.
45 *
46 * @since 3.3.7
47 */
48 public function __construct() {
49
50 $this->settings = new ConvertKit_Settings();
51
52 }
53
54 /**
55 * Enqueues the Cloudflare Turnstile client-side script if Cloudflare Turnstile
56 * site and secret keys are set and scripts are enabled.
57 *
58 * @since 3.3.7
59 */
60 public function enqueue_scripts() {
61
62 // Don't run if Cloudflare Turnstile or scripts are disabled.
63 if ( ! $this->settings->has_cloudflare_turnstile_site_and_secret_keys() || $this->settings->scripts_disabled() ) {
64 return;
65 }
66
67 // Enqueue Cloudflare Turnstile JS.
68 add_filter(
69 'convertkit_output_scripts_footer',
70 function ( $scripts ) {
71
72 $scripts[] = array(
73 'src' => self::CLIENT_SCRIPT_URL,
74 'async' => true,
75 'defer' => true,
76 );
77
78 return $scripts;
79
80 }
81 );
82
83 }
84
85 /**
86 * Verifies a Cloudflare Turnstile response token against the Siteverify API,
87 * if Cloudflare Turnstile site and secret keys are set, and scripts are enabled.
88 *
89 * Mirrors the request format documented at
90 * https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
91 *
92 * @since 3.3.7
93 *
94 * @param string $cloudflare_turnstile_response Cloudflare Turnstile response token from the client.
95 * @param string $plugin_action Plugin action string (unused).
96 * @return bool|WP_Error
97 */
98 public function verify( $cloudflare_turnstile_response, $plugin_action ) {
99
100 unset( $plugin_action );
101
102 // Don't run if Turnstile or scripts are disabled.
103 if ( ! $this->settings->has_cloudflare_turnstile_site_and_secret_keys() || $this->settings->scripts_disabled() ) {
104 return true;
105 }
106
107 // POST to Cloudflare Siteverify.
108 $response = wp_remote_post(
109 self::SITEVERIFY_URL,
110 array(
111 'body' => array(
112 'secret' => $this->settings->cloudflare_turnstile_secret_key(),
113 'response' => $cloudflare_turnstile_response,
114 'remoteip' => ( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '' ),
115 ),
116 )
117 );
118
119 // Bail if the request itself errored.
120 if ( is_wp_error( $response ) ) {
121 return $response;
122 }
123
124 // Decode response.
125 $body = json_decode( wp_remote_retrieve_body( $response ), true );
126
127 // If the response body couldn't be decoded, treat that as a failure.
128 if ( ! is_array( $body ) ) {
129 return new WP_Error(
130 'convertkit_cloudflare_turnstile_failed',
131 __( 'Cloudflare Turnstile failure: invalid response from Siteverify.', 'convertkit' )
132 );
133 }
134
135 // If the token verified, return true.
136 if ( $body['success'] === true ) {
137 return true;
138 }
139
140 // Return an error.
141 return new WP_Error(
142 'convertkit_cloudflare_turnstile_failed',
143 sprintf(
144 /* translators: Error codes */
145 __( 'Cloudflare Turnstile failure: %s', 'convertkit' ),
146 implode( ', ', $body['error-codes'] )
147 )
148 );
149
150 }
151
152 /**
153 * Inserts a Cloudflare Turnstile widget div immediately before the given
154 * submit button within an existing DOM tree. `data-appearance=interaction-only`
155 * keeps the widget invisible unless Cloudflare determines a challenge is
156 * required, and the `convertKitTurnstileFormSubmit` callback submits the
157 * enclosing form once the challenge is solved.
158 *
159 * @since 3.3.7
160 *
161 * @param ConvertKit_HTML_Parser $parser Parser wrapping the DOM.
162 * @param DOMElement $button <button> element.
163 * @param string $plugin_action Plugin action string (unused).
164 */
165 public function attach_to_form_button_dom( $parser, $button, $plugin_action ) {
166
167 unset( $plugin_action );
168
169 $widget = $parser->html->createElement( 'div' );
170 $widget->setAttribute( 'class', 'cf-turnstile' );
171 $widget->setAttribute( 'data-sitekey', esc_attr( $this->settings->cloudflare_turnstile_site_key() ) );
172 $widget->setAttribute( 'data-appearance', 'interaction-only' );
173 $widget->setAttribute( 'data-callback', 'convertKitTurnstileFormSubmit' );
174 $button->parentNode->insertBefore( $widget, $button ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
175
176 }
177
178 /**
179 * Returns the HTML for a Cloudflare Turnstile widget div followed by a
180 * plain submit button, used by templates that don't have a DOM parser
181 * available (e.g. the Restrict Content tag view).
182 *
183 * @since 3.3.7
184 *
185 * @param string $label The button's visible label.
186 * @param string $plugin_action The plugin action string (unused).
187 * @param string[] $css_classes CSS classes for the button.
188 * @return string
189 */
190 public function get_submit_button_html( $label, $plugin_action, $css_classes = array() ) {
191
192 unset( $plugin_action );
193
194 return sprintf(
195 '<div class="cf-turnstile" data-sitekey="%1$s" data-appearance="interaction-only" data-callback="convertKitTurnstileFormSubmit"></div><input type="submit" class="%2$s" value="%3$s" />',
196 esc_attr( $this->settings->cloudflare_turnstile_site_key() ),
197 esc_attr( implode( ' ', $css_classes ) ),
198 esc_attr( $label )
199 );
200
201 }
202
203 }
204