Make WordPress Core

Changeset 49334


Ignore:
Timestamp:
10/27/2020 06:30:03 PM (6 years ago)
Author:
TimothyBlynJacobs
Message:

Site Health, App Passwords: Test if the Authorization header is populated correctly.

App Passwords rely on the Authorization header to transport the Basic Auth credentials. For Apache web servers, WordPress automatically includes a RewriteRule to populate the value for servers running in CGI or FastCGI that wouldn't ordinarily populate the value.

This tests if the header is being filled with the expected values. For Apache users, we direct the user to visit the Permalinks settings to flush their permalinks. For all other users, we direct them to a help document on developer.wordpress.org.

Props Clorith, marybaum, TimothyBlynJacobs.
Fixes #51638.

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/js/_enqueues/admin/site-health.js

    r49154 r49334  
    213213                                if ( 'undefined' !== typeof( this.has_rest ) && this.has_rest ) {
    214214                                        wp.apiRequest( {
    215                                                 url: this.test
     215                                                url: this.test,
     216                                                headers: this.headers
    216217                                        } )
    217218                                                .done( function( response ) {
  • trunk/src/wp-admin/includes/class-wp-site-health.php

    r49266 r49334  
    136136                                                'has_rest'  => ( isset( $test['has_rest'] ) ? $test['has_rest'] : false ),
    137137                                                'completed' => false,
     138                                                'headers'   => isset( $test['headers'] ) ? $test['headers'] : array(),
    138139                                        );
    139140                                }
    … …  
    20742075                        );
    20752076                        return $result;
     2077                }
     2078
     2079                return $result;
     2080        }
     2081
     2082        /**
     2083         * Tests if the Authorization header has the expected values.
     2084         *
     2085         * @since 5.6.0
     2086         *
     2087         * @return array
     2088         */
     2089        public function get_test_authorization_header() {
     2090                $result = array(
     2091                        'label'       => __( 'The Authorization header is working as expected.' ),
     2092                        'status'      => 'good',
     2093                        'badge'       => array(
     2094                                'label' => __( 'Security' ),
     2095                                'color' => 'blue',
     2096                        ),
     2097                        'description' => sprintf(
     2098                                '<p>%s</p>',
     2099                                __( 'The Authorization header comes from the third-party applications you approve. Without it, those apps cannot connect to your site.' )
     2100                        ),
     2101                        'actions'     => '',
     2102                        'test'        => 'authorization_header',
     2103                );
     2104
     2105                if ( ! isset( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) {
     2106                        $result['label'] = __( 'The authorization header is missing.' );
     2107                } elseif ( 'user' !== $_SERVER['PHP_AUTH_USER'] || 'pwd' !== $_SERVER['PHP_AUTH_PW'] ) {
     2108                        $result['label'] = __( 'The authorization header is invalid.' );
     2109                } else {
     2110                        return $result;
     2111                }
     2112
     2113                $result['status'] = 'recommended';
     2114
     2115                if ( ! function_exists( 'got_mod_rewrite' ) ) {
     2116                        require_once ABSPATH . 'wp-admin/includes/misc.php';
     2117                }
     2118
     2119                if ( got_mod_rewrite() ) {
     2120                        $result['actions'] .= sprintf(
     2121                                '<p><a href="%s">%s</a></p>',
     2122                                esc_url( admin_url( 'options-permalink.php' ) ),
     2123                                __( 'Flush permalinks' )
     2124                        );
     2125                } else {
     2126                        $result['actions'] .= sprintf(
     2127                                '<p><a href="%s" target="_blank" rel="noopener">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
     2128                                'https://developer.wordpress.org/rest-api/frequently-asked-questions/#why-is-authentication-not-working',
     2129                                __( 'Learn how to configure the Authorization header.' ),
     2130                                /* translators: Accessibility text. */
     2131                                __( '(opens in a new tab)' )
     2132                        );
    20762133                }
    20772134
    … …  
    21782235                                        'async_direct_test' => array( WP_Site_Health::get_instance(), 'get_test_loopback_requests' ),
    21792236                                ),
     2237                                'authorization_header' => array(
     2238                                        'label'     => __( 'Authorization header' ),
     2239                                        'test'      => rest_url( 'wp-site-health/v1/tests/authorization-header' ),
     2240                                        'has_rest'  => true,
     2241                                        'headers'   => array( 'Authorization' => 'Basic ' . base64_encode( 'user:pwd' ) ),
     2242                                        'skip_cron' => true,
     2243                                ),
    21802244                        ),
    21812245                );
    … …  
    22042268                 * @since 5.2.0
    22052269                 * @since 5.6.0 Added the `async_direct_test` array key.
     2270                 *              Added the `skip_cron` array key.
    22062271                 *
    22072272                 * @param array $test_type {
    … …  
    22182283                 *                                           to be called to perform an async test.
    22192284                 *         @type boolean  $has_rest          Optional. Denote if `$test` has a REST API endpoint.
     2285                 *         @type boolean  $skip_cron         Whether to skip this test when running as cron.
    22202286                 *         @type callable $async_direct_test A manner of directly calling the test marked as asynchronous,
    22212287                 *                                           as the scheduled event can not authenticate, and endpoints
    … …  
    25582624
    25592625                foreach ( $tests['async'] as $test ) {
     2626                        if ( ! empty( $test['skip_cron'] ) ) {
     2627                                continue;
     2628                        }
     2629
    25602630                        // Local endpoints may require authentication, so asynchronous tests can pass a direct test runner as well.
    25612631                        if ( ! empty( $test['async_direct_test'] ) && is_callable( $test['async_direct_test'] ) ) {
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php

    r49225 r49334  
    9999                                        'permission_callback' => function () {
    100100                                                return $this->validate_request_permission( 'dotorg_communication' );
     101                                        },
     102                                ),
     103                                'schema' => array( $this, 'get_public_item_schema' ),
     104                        )
     105                );
     106
     107                register_rest_route(
     108                        $this->namespace,
     109                        sprintf(
     110                                '/%s/%s',
     111                                $this->rest_base,
     112                                'authorization-header'
     113                        ),
     114                        array(
     115                                array(
     116                                        'methods'             => 'GET',
     117                                        'callback'            => array( $this, 'test_authorization_header' ),
     118                                        'permission_callback' => function () {
     119                                                return $this->validate_request_permission( 'authorization_header' );
    101120                                        },
    102121                                ),
    … …  
    176195        public function test_loopback_requests() {
    177196                return $this->site_health->get_test_loopback_requests();
     197        }
     198
     199        /**
     200         * Checks that the authorization header is valid.
     201         *
     202         * @since 5.6.0
     203         *
     204         * @return array
     205         */
     206        public function test_authorization_header() {
     207                return $this->site_health->get_test_authorization_header();
    178208        }
    179209
  • trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php

    r49154 r49334  
    137137                        '/wp-site-health/v1/tests/loopback-requests',
    138138                        '/wp-site-health/v1/tests/dotorg-communication',
     139                        '/wp-site-health/v1/tests/authorization-header',
    139140                        '/wp-site-health/v1/directory-sizes',
    140141                );
Note: See TracChangeset for help on using the changeset viewer.