Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 54 additions & 11 deletions includes/Core/Authentication/Setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,26 +122,69 @@ public function register() {
*
* @since 1.81.0
* @since 1.167.0 Added support for custom error codes.
* @since n.e.x.t Added support for message-based error IDs.
*
* @param string|null $error_code The error code. Optional. Defaults to null.
* @param WP_Error|null $error Optional. The proxy request error. Defaults to null.
* @return string The get help link.
*/
private function get_oauth_proxy_failed_help_link( $error_code = null ) {
// Map `request_failed` to the error ID `request_to_auth_proxy_failed` for backwards compatibility.
if ( null === $error_code || 'request_failed' === $error_code ) {
$error_id = 'request_to_auth_proxy_failed';
} else {
$error_id = $error_code;
}

private function get_oauth_proxy_failed_help_link( $error = null ) {
return sprintf(
/* translators: 1: Support link URL. 2: Get help string. */
__( '<a href="%1$s" target="_blank">%2$s</a>', 'google-site-kit' ),
esc_url( add_query_arg( 'error_id', $error_id, $this->proxy_support_link_url ) ),
esc_url( add_query_arg( 'error_id', $this->get_oauth_proxy_failed_error_id( $error ), $this->proxy_support_link_url ) ),
esc_html__( 'Get help', 'google-site-kit' )
);
}

/**
* Resolves the support error_id for a failed oAuth proxy request.
*
* @since n.e.x.t
*
* @param WP_Error|null $error Optional. The proxy request error. Defaults to null.
* @return string The error_id query value for the support link.
*/
private function get_oauth_proxy_failed_error_id( $error = null ) {
if ( ! is_wp_error( $error ) ) {
return 'request_to_auth_proxy_failed';
}

$code = (string) $error->get_error_code();
$message = (string) $error->get_error_message();

// Prefer message patterns for transport failures that share a generic code.
if ( false !== stripos( $message, 'cURL error 7' ) ) {
return 'curl-error-7-operation-timed-out';
}

if ( false !== stripos( $message, 'cURL error 28' ) ) {
return 'curl-error-28-operation-timed-out';
}

// WP core uses this code when WP_HTTP_BLOCK_EXTERNAL blocks the request.
if ( 'http_request_not_executed' === $code
|| false !== stripos( $message, 'User has blocked requests through HTTP' )
) {
return 'user-has-blocked-requests-through-http';
}

if ( false !== stripos( $message, 'A site with the given URL already exists' ) ) {
return 'a-site-with-the-given-url-already-exists';
}

if ( false !== stripos( $message, 'Unable to retrieve site connection' ) ) {
return 'unable-to-retrieve-site-connection-you-may-need-to-perform-a-plugin-reset';
}

// Map `request_failed` to the parent guide ID for backwards compatibility.
if ( '' === $code || 'request_failed' === $code ) {
return 'request_to_auth_proxy_failed';
}

// Pass through custom proxy error codes (e.g. site-not-found).
return $code;
}

/**
* Handles the setup start action, taking the user to the proxy setup screen.
*
Expand Down Expand Up @@ -180,7 +223,7 @@ public function handle_action_setup_start() {
esc_html__( 'The request to the authentication proxy has failed with an error: %1$s %2$s.', 'google-site-kit' ),
esc_html( $error_message ),
wp_kses(
$this->get_oauth_proxy_failed_help_link( $oauth_setup_redirect->get_error_code() ),
$this->get_oauth_proxy_failed_help_link( $oauth_setup_redirect ),
array(
'a' => array(
'href' => array(),
Expand Down
53 changes: 47 additions & 6 deletions tests/phpunit/integration/Core/Authentication/Setup_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ public function test_handle_action_setup_start__wp_error( $params ) {
$has_credentials = $params['has_credentials'];
$error_code = $params['error_code'];
$expected_error_code = $params['expected_error_code'];
$error_message = array_key_exists( 'error_message', $params )
? $params['error_message']
: ( $has_credentials ? 'Test error message.' : null );

$user_id = $this->factory()->user->create( array( 'role' => 'administrator' ) );
wp_set_current_user( $user_id );
Expand All @@ -195,16 +198,13 @@ public function test_handle_action_setup_start__wp_error( $params ) {
// Fake a WP_Error IF a request is made to the Google Proxy server.
add_filter(
'pre_http_request',
function ( $preempt, $args, $url ) use ( $context, &$proxy_server_requests, $has_credentials, $error_code ) {
function ( $preempt, $args, $url ) use ( $context, &$proxy_server_requests, $error_code, $error_message ) {
if ( ( new Google_Proxy( $context ) )->url( Google_Proxy::OAUTH2_SITE_URI ) !== $url ) {
return $preempt;
}
// Collect any HTTP requests to the proxy server to register/sync site with the proxy server.
$proxy_server_requests[] = $args;

// Using the two cases for $has_credentials, we can test the error message
// and the fallback to an error code when there is no message.
$error_message = $has_credentials ? 'Test error message.' : null;
return new WP_Error( $error_code, $error_message );
},
10,
Expand All @@ -217,11 +217,12 @@ function ( $preempt, $args, $url ) use ( $context, &$proxy_server_requests, $has
} catch ( RedirectException $redirect ) {
$this->fail( 'Expected WPDieException!' );
} catch ( WPDieException $exception ) {
$error = $has_credentials ? 'Test error message.' : $error_code;
// Empty messages fall back to the error code in the die message.
$displayed_error = ! empty( $error_message ) ? $error_message : $error_code;
$this->assertStringContainsString(
sprintf(
'The request to the authentication proxy has failed with an error: %1$s <a href="https://sitekit.withgoogle.com/support?error_id=%2$s" target="_blank">Get help</a>.',
$error,
$displayed_error,
$expected_error_code
),
$exception->getMessage(),
Expand Down Expand Up @@ -262,6 +263,46 @@ public function data_conditionally_throws_error() {
'expected_error_code' => 'request_to_auth_proxy_failed',
),
),
'curl error 7 message' => array(
array(
'has_credentials' => true,
'error_code' => 'http_request_failed',
'error_message' => 'cURL error 7: Failed to connect to sitekit.withgoogle.com',
'expected_error_code' => 'curl-error-7-operation-timed-out',
),
),
'curl error 28 message' => array(
array(
'has_credentials' => true,
'error_code' => 'http_request_failed',
'error_message' => 'cURL error 28: Operation timed out after 5000 milliseconds',
'expected_error_code' => 'curl-error-28-operation-timed-out',
),
),
'blocked http requests by code' => array(
array(
'has_credentials' => true,
'error_code' => 'http_request_not_executed',
'error_message' => 'User has blocked requests through HTTP.',
'expected_error_code' => 'user-has-blocked-requests-through-http',
),
),
'site url already exists message' => array(
array(
'has_credentials' => true,
'error_code' => 'request_failed',
'error_message' => 'A site with the given URL already exists',
'expected_error_code' => 'a-site-with-the-given-url-already-exists',
),
),
'unable to retrieve site connection message' => array(
array(
'has_credentials' => true,
'error_code' => 'request_failed',
'error_message' => 'Unable to retrieve site connection. You may need to perform a plugin reset',
'expected_error_code' => 'unable-to-retrieve-site-connection-you-may-need-to-perform-a-plugin-reset',
),
),
);
}

Expand Down