| @@ -12,8 +12,12 @@ | ||
| 12 | 12 | /** |
| 13 | 13 | * Model for Captcha settings. |
| 14 | 14 | */ |
| 15 | 15 | class Captcha extends Setting { |
| 16 | + /** | |
| 17 | + * Turnstile provider key. | |
| 18 | + */ | |
| 19 | + const TURNSTILE = 'turnstile'; | |
| 16 | 20 | |
| 17 | 21 | /** |
| 18 | 22 | * Option name. |
| 19 | 23 | * We'll keep the current table for all Captcha for backward compatibility. There's no need to edit the table name to store settings in the database. |
| @@ -155,9 +159,9 @@ | ||
| 155 | 159 | * @var array |
| 156 | 160 | */ |
| 157 | 161 | protected $rules = array( |
| 158 | 162 | array( array( 'enabled', 'detect_woo', 'detect_buddypress' ), 'boolean' ), |
| 159 | - array( array( 'active_type' ), 'in', array( 'v2_checkbox', 'v2_invisible', 'v3_recaptcha', 'turnstile' ) ), | |
| 163 | + array( array( 'active_type' ), 'in', array( 'v2_checkbox', 'v2_invisible', 'v3_recaptcha', self::TURNSTILE ) ), | |
| 160 | 164 | ); |
| 161 | 165 | |
| 162 | 166 | /** |
| 163 | 167 | * Retrieves the default values for the Captcha settings. |
| @@ -181,25 +185,29 @@ | ||
| 181 | 185 | $this->provider = 'recaptcha'; |
| 182 | 186 | $this->message = $default_values['message']; |
| 183 | 187 | $this->language = 'automatic'; |
| 184 | 188 | $this->data_v2_checkbox = array( |
| 185 | - 'key' => '', | |
| 186 | - 'secret' => '', | |
| 187 | - 'size' => 'normal', | |
| 188 | - 'style' => 'light', | |
| 189 | + 'key' => '', | |
| 190 | + 'secret' => '', | |
| 191 | + 'verified' => false, | |
| 192 | + 'size' => 'normal', | |
| 193 | + 'style' => 'light', | |
| 189 | 194 | ); |
| 190 | 195 | $this->data_v2_invisible = array( |
| 191 | - 'key' => '', | |
| 192 | - 'secret' => '', | |
| 196 | + 'key' => '', | |
| 197 | + 'secret' => '', | |
| 198 | + 'verified' => false, | |
| 193 | 199 | ); |
| 194 | 200 | $this->data_v3_recaptcha = array( |
| 195 | 201 | 'key' => '', |
| 196 | 202 | 'secret' => '', |
| 203 | + 'verified' => false, | |
| 197 | 204 | 'threshold' => '0.5', |
| 198 | 205 | ); |
| 199 | 206 | $this->data_turnstile = array( |
| 200 | 207 | 'key' => '', |
| 201 | 208 | 'secret' => '', |
| 209 | + 'verified' => false, | |
| 202 | 210 | 'size' => 'normal', |
| 203 | 211 | 'style' => 'auto', |
| 204 | 212 | 'message' => $default_values['turnstile_message'], |
| 205 | 213 | 'language' => 'auto', |
| @@ -206,8 +214,20 @@ | ||
| 206 | 214 | ); |
| 207 | 215 | } |
| 208 | 216 | |
| 209 | 217 | /** |
| 218 | + * Backfill new keys (e.g. 'verified') into data arrays loaded from settings saved by an older version. | |
| 219 | + * | |
| 220 | + * @return void | |
| 221 | + */ | |
| 222 | + protected function after_load(): void { | |
| 223 | + $this->data_v2_checkbox = wp_parse_args( $this->data_v2_checkbox, array( 'verified' => false ) ); | |
| 224 | + $this->data_v2_invisible = wp_parse_args( $this->data_v2_invisible, array( 'verified' => false ) ); | |
| 225 | + $this->data_v3_recaptcha = wp_parse_args( $this->data_v3_recaptcha, array( 'verified' => false ) ); | |
| 226 | + $this->data_turnstile = wp_parse_args( $this->data_turnstile, array( 'verified' => false ) ); | |
| 227 | + } | |
| 228 | + | |
| 229 | + /** | |
| 210 | 230 | * Checks if the given Captcha type is valid and has all the necessary data. |
| 211 | 231 | * |
| 212 | 232 | * @param string $active_type The Captcha type to check. |
| 213 | 233 | * |
| @@ -231,9 +251,9 @@ | ||
| 231 | 251 | && '' !== $this->data_v3_recaptcha['key'] && '' !== $this->data_v3_recaptcha['secret'] |
| 232 | 252 | ) { |
| 233 | 253 | return true; |
| 234 | 254 | } elseif ( |
| 235 | - 'turnstile' === $active_type && | |
| 255 | + self::TURNSTILE === $active_type && | |
| 236 | 256 | '' !== $this->data_turnstile['key'] && |
| 237 | 257 | '' !== $this->data_turnstile['secret'] |
| 238 | 258 | ) { |
| 239 | 259 | return true; |
| @@ -247,10 +267,10 @@ | ||
| 247 | 267 | * |
| 248 | 268 | * @return bool Returns true if the CAPTCHA is active, false otherwise. |
| 249 | 269 | */ |
| 250 | 270 | public function is_active(): bool { |
| 251 | - $hook_name = 'turnstile' === $this->active_type ? 'wd_turnstile_enable' : 'wd_recaptcha_enable'; | |
| 252 | - return (bool) apply_filters( | |
| 271 | + $hook_name = self::TURNSTILE === $this->active_type ? 'wd_turnstile_enable' : 'wd_recaptcha_enable'; | |
| 272 | + $enable = apply_filters( | |
| 253 | 273 | $hook_name, |
| 254 | 274 | $this->enabled |
| 255 | 275 | && '' !== $this->active_type |
| 256 | 276 | && '' !== $this->language |
| @@ -256,8 +276,10 @@ | ||
| 256 | 276 | && '' !== $this->language |
| 257 | 277 | // For each Captcha type. |
| 258 | 278 | && $this->check_captcha_type( $this->active_type ) |
| 259 | 279 | ); |
| 280 | + | |
| 281 | + return is_bool( $enable ) ? $enable : (bool) $enable; | |
| 260 | 282 | } |
| 261 | 283 | |
| 262 | 284 | /** |
| 263 | 285 | * Is activated any default location? |
| @@ -355,39 +377,8 @@ | ||
| 355 | 377 | ); |
| 356 | 378 | } |
| 357 | 379 | |
| 358 | 380 | /** |
| 359 | - * Validates the form after submission. | |
| 360 | - * | |
| 361 | - * @return void | |
| 362 | - */ | |
| 363 | - protected function after_validate(): void { | |
| 364 | - // Case with multi errors. | |
| 365 | - if ( $this->is_unchecked_woo_locations() && $this->is_unchecked_buddypress_locations() ) { | |
| 366 | - // The text of the notation is only in the first key, but we add the number of keys depending on the disabled locations of the plugins. | |
| 367 | - $this->errors['enable_woo'] = esc_html__( | |
| 368 | - 'You have enabled reCaptcha for more than one plugin. Please select at least one form location for each plugin and click Save Changes again.', | |
| 369 | - 'defender-security' | |
| 370 | - ); | |
| 371 | - $this->errors['enable_buddypress'] = ''; | |
| 372 | - } else { | |
| 373 | - // Individual cases with plugins. | |
| 374 | - if ( $this->is_unchecked_woo_locations() ) { | |
| 375 | - $this->errors['enable_woo'] = esc_html__( | |
| 376 | - 'reCAPTCHA for WooCommerce is enabled, but no WooCommerce forms are selected. Please select at least one WooCommerce form location and then click Save Changes again.', | |
| 377 | - 'defender-security' | |
| 378 | - ); | |
| 379 | - } | |
| 380 | - if ( $this->is_unchecked_buddypress_locations() ) { | |
| 381 | - $this->errors['enable_buddypress'] = esc_html__( | |
| 382 | - 'reCAPTCHA for BuddyPress is enabled, but no BuddyPress forms are selected. Please select at least one BuddyPress form location and then click Save Changes again.', | |
| 383 | - 'defender-security' | |
| 384 | - ); | |
| 385 | - } | |
| 386 | - } | |
| 387 | - } | |
| 388 | - | |
| 389 | - /** | |
| 390 | 381 | * Disable for logged in users or enable. |
| 391 | 382 | * |
| 392 | 383 | * @return bool |
| 393 | 384 | */ |
| @@ -401,6 +392,28 @@ | ||
| 401 | 392 | * @return string |
| 402 | 393 | */ |
| 403 | 394 | public static function get_module_name(): string { |
| 404 | 395 | return esc_html__( 'CAPTCHA', 'defender-security' ); |
| 396 | + } | |
| 397 | + | |
| 398 | + /** | |
| 399 | + * Get active captcha data based on provider and active type. | |
| 400 | + * | |
| 401 | + * @param string|null $provider The provider name. | |
| 402 | + * @param string|null $active_type The active type. | |
| 403 | + * | |
| 404 | + * @return array The captcha data for the specified provider. | |
| 405 | + */ | |
| 406 | + public function get_active_captcha_data( ?string $provider = null, ?string $active_type = null ): array { | |
| 407 | + $provider = $provider ?? $this->provider; | |
| 408 | + $active_type = $active_type ?? $this->active_type; | |
| 409 | + if ( self::TURNSTILE === $provider ) { | |
| 410 | + return $this->data_turnstile; | |
| 411 | + } | |
| 412 | + return match ( $active_type ) { | |
| 413 | + 'v2_checkbox' => $this->data_v2_checkbox, | |
| 414 | + 'v2_invisible' => $this->data_v2_invisible, | |
| 415 | + 'v3_recaptcha' => $this->data_v3_recaptcha, | |
| 416 | + default => array(), | |
| 417 | + }; | |
| 405 | 418 | } |
| 406 | 419 | } |