415 lines
10 KiB
PHP
415 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* API wrapper for the Recaptcha service.
|
|
*
|
|
* @since 1.0
|
|
* @package Gravity_Forms\Gravity_Forms_RECAPTCHA
|
|
*/
|
|
|
|
namespace Gravity_Forms\Gravity_Forms_RECAPTCHA;
|
|
|
|
use WP_Error;
|
|
|
|
/**
|
|
* Class RECAPTCHA_API
|
|
*
|
|
* @package Gravity_Forms\Gravity_Forms_RECAPTCHA
|
|
*/
|
|
class RECAPTCHA_API {
|
|
/**
|
|
* Google Recaptcha token verification URL.
|
|
*
|
|
* @since 1.0
|
|
* @var string
|
|
*/
|
|
private $verification_url = 'https://www.google.com/recaptcha/api/siteverify';
|
|
|
|
/**
|
|
* OAuth Access Token.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $access_token;
|
|
|
|
/**
|
|
* OAuth Refresh Token.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $refresh_token;
|
|
|
|
/**
|
|
* The GF_RECAPTCHA instance.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @var GF_RECAPTCHA|null The GF_RECAPTCHA instance.
|
|
*/
|
|
protected $addon;
|
|
|
|
/**
|
|
* The Google Cloud Project ID.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $project_id;
|
|
|
|
/**
|
|
* Constructor for RECAPTCHA_API
|
|
*
|
|
* @param string $auth_data The array of auth data.
|
|
* @param GF_RECAPTCHA $addon The GF_RECAPTCHA instance.
|
|
*/
|
|
public function __construct( $auth_data = null, $addon = null ) {
|
|
$this->addon = $addon;
|
|
$this->refresh_token = rgar( $auth_data, 'refresh_token' );
|
|
$this->access_token = rgar( $auth_data, 'access_token' );
|
|
$this->project_id = rgar( $auth_data, 'project_id' );
|
|
}
|
|
|
|
/**
|
|
* Get Gravity API URL for path.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @param string $path Endpoint path.
|
|
*
|
|
* @return string URL for Gravity API endpoint.
|
|
*/
|
|
public static function get_gravity_api_url( $path = '' ) {
|
|
|
|
if ( '/' !== substr( $path, 0, 1 ) ) {
|
|
$path = '/' . $path;
|
|
}
|
|
|
|
return defined( 'GRAVITY_API_URL' ) ? GRAVITY_API_URL . '/auth/googlerecaptcha' . $path : self::$gravity_api_url . '/auth/googlerecaptcha' . $path;
|
|
}
|
|
|
|
/**
|
|
* Get the base URL for the Recaptcha API.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @param string $base_path The base path.
|
|
*
|
|
* @return string
|
|
*/
|
|
private function get_base_url( $base_path = null ) {
|
|
if ( $base_path ) {
|
|
return $base_path;
|
|
}
|
|
|
|
return 'https://recaptchaenterprise.googleapis.com/v1/';
|
|
}
|
|
|
|
/**
|
|
* Make a request to the reCAPTCHA API.
|
|
*
|
|
* @param string $path The relative request path.
|
|
* @param array $body The request body.
|
|
* @param array $headers The request headers.
|
|
* @param string $method The request method.
|
|
* @param string $base_path The base API path.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @return array|string|WP_Error
|
|
*/
|
|
private function make_request( $path = '', $body = array(), $headers = array(), $method = 'GET', $base_path = null ) {
|
|
|
|
gf_recaptcha()->log_debug( __METHOD__ . '(): Making request to: ' . $path );
|
|
|
|
// Build request URL.
|
|
$request_url = $this->get_base_url( $base_path ) . $path;
|
|
|
|
$args = array(
|
|
'method' => $method,
|
|
/**
|
|
* Filters if SSL verification should occur.
|
|
*
|
|
* @param bool $ssl_verify If the SSL certificate should be verified. Defaults to false.
|
|
* @param string $request_url The request URL.
|
|
*
|
|
* @return bool
|
|
*/
|
|
'sslverify' => apply_filters( 'https_local_ssl_verify', false, $request_url ),
|
|
/**
|
|
* Sets the HTTP timeout, in seconds, for the request.
|
|
*
|
|
* @param int $timeout_value The timeout limit, in seconds. Defaults to 30.
|
|
* @param string $request_url The request URL.
|
|
*
|
|
* @return int
|
|
*/
|
|
'timeout' => apply_filters( 'http_request_timeout', 30, $request_url ),
|
|
);
|
|
|
|
$args['headers'] = $headers;
|
|
|
|
if ( 'GET' === $method || 'POST' === $method ) {
|
|
$args['body'] = empty( $body ) ? '' : $body;
|
|
}
|
|
|
|
if ( 'POST' === $method ) {
|
|
$args['body'] = wp_json_encode( $body );
|
|
}
|
|
|
|
// Execute request.
|
|
$response = wp_remote_request(
|
|
$request_url,
|
|
$args
|
|
);
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
}
|
|
|
|
$response_body = wp_remote_retrieve_body( $response );
|
|
$retrieved_response_code = wp_remote_retrieve_response_code( $response );
|
|
|
|
if ( 200 !== $retrieved_response_code ) {
|
|
$error_message = rgars( $response_body, 'error/message', "Expected response code: 200. Returned response code: {$retrieved_response_code}." );
|
|
$error_code = rgars( $response_body, 'error/errors/reason', 'google_recaptcha_api_error' );
|
|
|
|
gf_recaptcha()->log_error( __METHOD__ . '(): Unable to validate with the Google Cloud API: ' . $error_message );
|
|
|
|
return new WP_Error( $error_code, $error_message, $retrieved_response_code );
|
|
}
|
|
|
|
return $response_body;
|
|
}
|
|
|
|
/**
|
|
* Get the result of token verification from the Recaptcha API.
|
|
*
|
|
* @param string $token The token to verify.
|
|
* @param string $secret The site's secret key.
|
|
*
|
|
* @return array|\WP_Error
|
|
*/
|
|
public function verify_token( $token, $secret ) {
|
|
return wp_remote_post(
|
|
$this->verification_url,
|
|
array(
|
|
'body' => array(
|
|
'secret' => $secret,
|
|
'response' => $token,
|
|
),
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Create the reCAPTCHA enterprise assessment.
|
|
*
|
|
* @param string $access_token The OAuth access token.
|
|
* @param string $project_id The Google Cloud Project ID.
|
|
* @param string $token The reCAPTCHA token from the submission.
|
|
* @param string $site_key The reCAPTCHA site key.
|
|
* @param string $action Teh reCATPCHA action.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @return array|mixed|WP_Error
|
|
*/
|
|
public function create_recaptcha_assessment( $access_token, $project_id, $token, $site_key, $action ) {
|
|
|
|
$url = 'https://recaptchaenterprise.googleapis.com/v1/projects/' . $project_id . '/assessments';
|
|
|
|
$payload = wp_json_encode(
|
|
array(
|
|
'event' => array(
|
|
'token' => $token,
|
|
'siteKey' => $site_key,
|
|
'expectedAction' => $action,
|
|
),
|
|
)
|
|
);
|
|
|
|
$response = wp_remote_post(
|
|
$url,
|
|
array(
|
|
'headers' => array(
|
|
'Authorization' => 'Bearer ' . $access_token,
|
|
'Content-Type' => 'application/json',
|
|
),
|
|
'body' => $payload,
|
|
)
|
|
);
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
}
|
|
|
|
$body = json_decode( wp_remote_retrieve_body( $response ), true );
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* Annotates the assessment; informing Google that the submission was spam or ham.
|
|
*
|
|
* @since 2.0
|
|
*
|
|
* @param string $assessment_id The assessment ID
|
|
* @param string $annotation The annotation to added to the assessment. Possible values: LEGITIMATE or FRAUDULENT.
|
|
*
|
|
* @return array|mixed|WP_Error
|
|
*/
|
|
public function annotate_assessment( $assessment_id, $annotation ) {
|
|
$body = array(
|
|
'annotation' => $annotation,
|
|
);
|
|
|
|
$response = wp_remote_post(
|
|
sprintf( 'https://recaptchaenterprise.googleapis.com/v1/%s:annotate', $assessment_id ),
|
|
array(
|
|
'headers' => array(
|
|
'Authorization' => 'Bearer ' . $this->access_token,
|
|
'Content-Type' => 'application/json',
|
|
),
|
|
'body' => wp_json_encode( $body ),
|
|
)
|
|
);
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
}
|
|
|
|
$body = wp_remote_retrieve_body( $response );
|
|
|
|
return $body ? json_decode( $body, true ) : $body;
|
|
}
|
|
|
|
/**
|
|
* Refresh the reCAPTCHA access token.
|
|
*
|
|
* @param string $refresh_token The refresh token.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @return array|WP_Error
|
|
*/
|
|
public function refresh_token( $refresh_token ) {
|
|
// Connect to Gravity Form's API.
|
|
$response = wp_remote_post(
|
|
$this->get_gravity_api_url( 'refresh' ),
|
|
array(
|
|
'body' => array(
|
|
'refresh_token' => rawurlencode( $refresh_token ),
|
|
),
|
|
)
|
|
);
|
|
if ( is_wp_error( $response ) ) {
|
|
return $response;
|
|
}
|
|
|
|
$retrieved_response_code = wp_remote_retrieve_response_code( $response );
|
|
if ( 200 !== $retrieved_response_code ) {
|
|
$error_message = "Expected response code: 200. Returned response code: {$retrieved_response_code}.";
|
|
|
|
return new WP_Error( 'google_recaptcha_api_error', $error_message, $retrieved_response_code );
|
|
}
|
|
|
|
$response_body = gf_recaptcha()->maybe_decode_json( wp_remote_retrieve_body( $response ) );
|
|
|
|
if ( array_key_exists( 'auth_error', $response_body ) ) {
|
|
if ( empty( $response_body['auth_error'] ) ) {
|
|
$error_message = 'Google returned an empty response.';
|
|
} else {
|
|
$error_message = 'Google response: ' . print_r( $response_body['auth_error'], true );
|
|
}
|
|
|
|
return new WP_Error( 'google_recaptcha_api_error', $error_message );
|
|
}
|
|
|
|
return $response_body;
|
|
}
|
|
|
|
/**
|
|
* Get a list of Google Cloud projects.
|
|
*
|
|
* @since 1.7.0
|
|
*
|
|
* @return array|mixed
|
|
*/
|
|
public function get_recaptcha_projects() {
|
|
|
|
$request_path = 'projects';
|
|
|
|
$headers = array(
|
|
'Authorization' => 'Bearer ' . $this->access_token,
|
|
'Accept' => 'application/json',
|
|
);
|
|
|
|
$response = $this->make_request( $request_path, array(), $headers, 'GET', 'https://cloudresourcemanager.googleapis.com/v1/' );
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
return array();
|
|
}
|
|
|
|
$data = json_decode( $response, true );
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Get a list of enterprise site keys associates with the chosen project.
|
|
*
|
|
* @param string $project The Google Cloud Project ID.
|
|
*
|
|
* @since 1.7.0
|
|
* @since 1.8.0 Increased the default page size to 100 and added pagination to retrieve all keys.
|
|
*
|
|
* @return array|mixed
|
|
*/
|
|
public function get_enterprise_site_keys( $project ) {
|
|
$request_path_base = 'projects/' . $project . '/keys';
|
|
|
|
$headers = array(
|
|
'Authorization' => 'Bearer ' . $this->access_token,
|
|
'Accept' => 'application/json',
|
|
);
|
|
|
|
$all_keys = array(
|
|
'keys' => array(),
|
|
);
|
|
$page_token = null;
|
|
$page_size = apply_filters( 'gform_recaptcha_enterprise_keys_page_size', 100 );
|
|
|
|
$query_params = array(
|
|
'pageSize' => $page_size,
|
|
);
|
|
|
|
do {
|
|
if ( $page_token ) {
|
|
$query_params['pageToken'] = $page_token;
|
|
}
|
|
|
|
$request_path = $request_path_base . '?' . http_build_query( $query_params );
|
|
|
|
$response = $this->make_request( $request_path, array(), $headers );
|
|
|
|
if ( is_wp_error( $response ) ) {
|
|
$this->addon->log_error( __METHOD__ . '(): Unable to retrieve site keys: ' . $response->get_error_message() );
|
|
break;
|
|
}
|
|
|
|
$data = json_decode( $response, true );
|
|
|
|
if ( isset( $data['keys'] ) && is_array( $data['keys'] ) ) {
|
|
$all_keys['keys'] = array_merge( $all_keys['keys'], $data['keys'] );
|
|
}
|
|
|
|
$page_token = rgar( $data, 'nextPageToken' ) ? $data['nextPageToken'] : null;
|
|
} while ( $page_token );
|
|
|
|
return $all_keys;
|
|
}
|
|
}
|