diff --git a/includes/Core/Authentication/Setup.php b/includes/Core/Authentication/Setup.php index 5764d29b182..cb542fef539 100644 --- a/includes/Core/Authentication/Setup.php +++ b/includes/Core/Authentication/Setup.php @@ -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. */ __( '%2$s', '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. * @@ -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(), diff --git a/tests/phpunit/integration/Core/Authentication/Setup_Test.php b/tests/phpunit/integration/Core/Authentication/Setup_Test.php index 8f6a5663002..ccda940d097 100644 --- a/tests/phpunit/integration/Core/Authentication/Setup_Test.php +++ b/tests/phpunit/integration/Core/Authentication/Setup_Test.php @@ -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 ); @@ -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, @@ -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 Get help.', - $error, + $displayed_error, $expected_error_code ), $exception->getMessage(), @@ -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', + ), + ), ); }