laipower/wp-content/plugins/jetpack-protect/jetpack_vendor/automattic/jetpack-connection/src/class-server-sandbox.php

245 lines
7.7 KiB
PHP

<?php
/**
* The Server_Sandbox class.
*
* This feature is only useful for Automattic developers.
* It configures Jetpack to talk to staging/sandbox servers
* on WordPress.com instead of production servers.
*
* @package automattic/jetpack-sandbox
*/
namespace Automattic\Jetpack\Connection;
use Automattic\Jetpack\Constants;
/**
* The Server_Sandbox class.
*/
class Server_Sandbox {
/**
* Sets up the action hooks for the server sandbox.
*/
public function init() {
if ( did_action( 'jetpack_server_sandbox_init' ) ) {
return;
}
add_action( 'requests-requests.before_request', array( $this, 'server_sandbox' ), 10, 4 );
add_action( 'admin_bar_menu', array( $this, 'admin_bar_add_sandbox_item' ), 999 );
/**
* Fires when the server sandbox is initialized. This action is used to ensure that
* the server sandbox action hooks are set up only once.
*
* @since 1.30.7
*/
do_action( 'jetpack_server_sandbox_init' );
}
/**
* Returns the new url and host values.
*
* @param string $sandbox Sandbox domain.
* @param string $url URL of request about to be made.
* @param array $headers Headers of request about to be made.
* @param string $data The body of request about to be made.
* @param string $method The method of request about to be made.
*
* @return array [ 'url' => new URL, 'host' => new Host, 'new_signature => New signature if url was changed ]
*/
public function server_sandbox_request_parameters( $sandbox, $url, $headers, $data = null, $method = 'GET' ) {
$host = '';
$new_signature = '';
if ( ! is_string( $sandbox ) || ! is_string( $url ) ) {
return array(
'url' => $url,
'host' => $host,
'new_signature' => $new_signature,
);
}
$url_host = wp_parse_url( $url, PHP_URL_HOST );
switch ( $url_host ) {
case 'public-api.wordpress.com':
case 'jetpack.wordpress.com':
case 'jetpack.com':
case 'dashboard.wordpress.com':
$host = isset( $headers['Host'] ) ? $headers['Host'] : $url_host;
$original_url = $url;
$url = preg_replace(
'@^(https?://)' . preg_quote( $url_host, '@' ) . '(?=[/?#].*|$)@',
'${1}' . $sandbox,
$url,
1
);
/**
* Whether to add the X Debug query parameter to the request made to the Sandbox
*
* @since 1.36.0
*
* @param bool $add_parameter Whether to add the parameter to the request or not. Default is to false.
* @param string $url The URL of the request being made.
* @param string $host The host of the request being made.
*/
if ( apply_filters( 'jetpack_sandbox_add_profile_parameter', false, $url, $host ) ) {
$url = add_query_arg( 'XDEBUG_PROFILE', 1, $url );
// URL has been modified since the signature was created. We'll need a new one.
$original_url = add_query_arg( 'XDEBUG_PROFILE', 1, $original_url );
$new_signature = $this->get_new_signature( $original_url, $headers, $data, $method );
}
}
return compact( 'url', 'host', 'new_signature' );
}
/**
* Gets a new signature for the request
*
* @param string $url The new URL to be signed.
* @param array $headers The headers of the request about to be made.
* @param string $data The body of request about to be made.
* @param string $method The method of the request about to be made.
* @return string|null
*/
private function get_new_signature( $url, $headers, $data, $method ) {
if ( ! empty( $headers['Authorization'] ) ) {
$a_headers = $this->extract_authorization_headers( $headers );
if ( ! empty( $a_headers ) ) {
$token_details = explode( ':', $a_headers['token'] );
if ( count( $token_details ) === 3 ) {
$user_id = $token_details[2];
$token = ( new Tokens() )->get_access_token( $user_id );
$time_diff = (int) \Jetpack_Options::get_option( 'time_diff' );
$jetpack_signature = new \Jetpack_Signature( $token->secret, $time_diff );
$signature = $jetpack_signature->sign_request(
$a_headers['token'],
$a_headers['timestamp'],
$a_headers['nonce'],
$a_headers['body-hash'],
$method,
$url,
$data,
false
);
if ( $signature && ! is_wp_error( $signature ) ) {
return $signature;
} elseif ( is_wp_error( $signature ) ) {
$this->log_new_signature_error( $signature->get_error_message() );
}
} else {
$this->log_new_signature_error( 'Malformed token on Authorization Header' );
}
} else {
$this->log_new_signature_error( 'Error extracting Authorization Header' );
}
} else {
$this->log_new_signature_error( 'Empty Authorization Header' );
}
}
/**
* Logs error if the attempt to create a new signature fails
*
* @param string $message The error message.
* @return void
*/
private function log_new_signature_error( $message ) {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( sprintf( "SANDBOXING: Error re-signing the request. '%s'", $message ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
}
}
/**
* Extract the values in the Authorization header into an array
*
* @param array $headers The headers of the request about to be made.
* @return array|null
*/
public function extract_authorization_headers( $headers ) {
if ( ! empty( $headers['Authorization'] ) && is_string( $headers['Authorization'] ) ) {
$header = str_replace( 'X_JETPACK ', '', $headers['Authorization'] );
$vars = explode( ' ', $header );
$result = array();
foreach ( $vars as $var ) {
$elements = explode( '"', $var );
if ( count( $elements ) === 3 ) {
$result[ substr( $elements[0], 0, -1 ) ] = $elements[1];
}
}
return $result;
}
}
/**
* Modifies parameters of request in order to send the request to the
* server specified by `JETPACK__SANDBOX_DOMAIN`.
*
* Attached to the `requests-requests.before_request` filter.
*
* @param string $url URL of request about to be made.
* @param array $headers Headers of request about to be made.
* @param array|string $data Data of request about to be made.
* @param string $type Type of request about to be made.
* @return void
*/
public function server_sandbox( &$url, &$headers, &$data = null, &$type = null ) {
if ( ! Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ) ) {
return;
}
$original_url = $url;
$request_parameters = $this->server_sandbox_request_parameters( Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ), $url, $headers, $data, $type );
$url = $request_parameters['url'];
if ( $request_parameters['host'] ) {
$headers['Host'] = $request_parameters['host'];
if ( $request_parameters['new_signature'] ) {
$headers['Authorization'] = preg_replace( '/signature=\"[^\"]+\"/', 'signature="' . $request_parameters['new_signature'] . '"', $headers['Authorization'] );
}
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( sprintf( "SANDBOXING via '%s': '%s'", Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ), $original_url ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
}
}
}
/**
* Adds a "Jetpack API Sandboxed" item to the admin bar if the JETPACK__SANDBOX_DOMAIN
* constant is set.
*
* Attached to the `admin_bar_menu` action.
*
* @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
*/
public function admin_bar_add_sandbox_item( $wp_admin_bar ) {
if ( ! Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ) ) {
return;
}
$node = array(
'id' => 'jetpack-connection-api-sandbox',
'title' => 'Jetpack API Sandboxed',
'meta' => array(
'title' => 'Sandboxing via ' . Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ),
),
);
$wp_admin_bar->add_menu( $node );
}
}