diff --git a/src/js/_enqueues/admin/site-health.js b/src/js/_enqueues/admin/site-health.js index 54497464cd..3314f41e1e 100644 --- a/src/js/_enqueues/admin/site-health.js +++ b/src/js/_enqueues/admin/site-health.js @@ -212,7 +212,8 @@ jQuery( document ).ready( function( $ ) { if ( 'undefined' !== typeof( this.has_rest ) && this.has_rest ) { wp.apiRequest( { - url: this.test + url: this.test, + headers: this.headers } ) .done( function( response ) { /** This filter is documented in wp-admin/includes/class-wp-site-health.php */ diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index 2e556f40a8..19491d2eed 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -135,6 +135,7 @@ class WP_Site_Health { 'test' => $test['test'], 'has_rest' => ( isset( $test['has_rest'] ) ? $test['has_rest'] : false ), 'completed' => false, + 'headers' => isset( $test['headers'] ) ? $test['headers'] : array(), ); } } @@ -2078,6 +2079,62 @@ class WP_Site_Health { return $result; } + /** + * Tests if the Authorization header has the expected values. + * + * @since 5.6.0 + * + * @return array + */ + public function get_test_authorization_header() { + $result = array( + 'label' => __( 'The Authorization header is working as expected.' ), + 'status' => 'good', + 'badge' => array( + 'label' => __( 'Security' ), + 'color' => 'blue', + ), + 'description' => sprintf( + '

%s

', + __( 'The Authorization header comes from the third-party applications you approve. Without it, those apps cannot connect to your site.' ) + ), + 'actions' => '', + 'test' => 'authorization_header', + ); + + if ( ! isset( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) { + $result['label'] = __( 'The authorization header is missing.' ); + } elseif ( 'user' !== $_SERVER['PHP_AUTH_USER'] || 'pwd' !== $_SERVER['PHP_AUTH_PW'] ) { + $result['label'] = __( 'The authorization header is invalid.' ); + } else { + return $result; + } + + $result['status'] = 'recommended'; + + if ( ! function_exists( 'got_mod_rewrite' ) ) { + require_once ABSPATH . 'wp-admin/includes/misc.php'; + } + + if ( got_mod_rewrite() ) { + $result['actions'] .= sprintf( + '

%s

', + esc_url( admin_url( 'options-permalink.php' ) ), + __( 'Flush permalinks' ) + ); + } else { + $result['actions'] .= sprintf( + '

%s %s

', + 'https://developer.wordpress.org/rest-api/frequently-asked-questions/#why-is-authentication-not-working', + __( 'Learn how to configure the Authorization header.' ), + /* translators: Accessibility text. */ + __( '(opens in a new tab)' ) + ); + } + + return $result; + } + /** * Return a set of tests that belong to the site status page. * @@ -2177,6 +2234,13 @@ class WP_Site_Health { 'has_rest' => true, 'async_direct_test' => array( WP_Site_Health::get_instance(), 'get_test_loopback_requests' ), ), + 'authorization_header' => array( + 'label' => __( 'Authorization header' ), + 'test' => rest_url( 'wp-site-health/v1/tests/authorization-header' ), + 'has_rest' => true, + 'headers' => array( 'Authorization' => 'Basic ' . base64_encode( 'user:pwd' ) ), + 'skip_cron' => true, + ), ), ); @@ -2203,6 +2267,7 @@ class WP_Site_Health { * * @since 5.2.0 * @since 5.6.0 Added the `async_direct_test` array key. + * Added the `skip_cron` array key. * * @param array $test_type { * An associative array, where the `$test_type` is either `direct` or @@ -2217,6 +2282,7 @@ class WP_Site_Health { * @type mixed $test A callable to perform a direct test, or a string AJAX action * to be called to perform an async test. * @type boolean $has_rest Optional. Denote if `$test` has a REST API endpoint. + * @type boolean $skip_cron Whether to skip this test when running as cron. * @type callable $async_direct_test A manner of directly calling the test marked as asynchronous, * as the scheduled event can not authenticate, and endpoints * may require authentication. @@ -2557,6 +2623,10 @@ class WP_Site_Health { } foreach ( $tests['async'] as $test ) { + if ( ! empty( $test['skip_cron'] ) ) { + continue; + } + // Local endpoints may require authentication, so asynchronous tests can pass a direct test runner as well. if ( ! empty( $test['async_direct_test'] ) && is_callable( $test['async_direct_test'] ) ) { // This test is callable, do so and continue to the next asynchronous check. diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php index 11c5151799..6dbc239ec0 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php @@ -104,6 +104,25 @@ class WP_REST_Site_Health_Controller extends WP_REST_Controller { ) ); + register_rest_route( + $this->namespace, + sprintf( + '/%s/%s', + $this->rest_base, + 'authorization-header' + ), + array( + array( + 'methods' => 'GET', + 'callback' => array( $this, 'test_authorization_header' ), + 'permission_callback' => function () { + return $this->validate_request_permission( 'authorization_header' ); + }, + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + register_rest_route( $this->namespace, sprintf( @@ -177,6 +196,17 @@ class WP_REST_Site_Health_Controller extends WP_REST_Controller { return $this->site_health->get_test_loopback_requests(); } + /** + * Checks that the authorization header is valid. + * + * @since 5.6.0 + * + * @return array + */ + public function test_authorization_header() { + return $this->site_health->get_test_authorization_header(); + } + /** * Gets the current directory sizes for this install. * diff --git a/tests/phpunit/tests/rest-api/rest-schema-setup.php b/tests/phpunit/tests/rest-api/rest-schema-setup.php index 086edeab9b..e9f88f6951 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-setup.php +++ b/tests/phpunit/tests/rest-api/rest-schema-setup.php @@ -136,6 +136,7 @@ class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase { '/wp-site-health/v1/tests/background-updates', '/wp-site-health/v1/tests/loopback-requests', '/wp-site-health/v1/tests/dotorg-communication', + '/wp-site-health/v1/tests/authorization-header', '/wp-site-health/v1/directory-sizes', );