<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[49334] trunk: Site Health, App Passwords: Test if the Authorization header is populated correctly.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="https://core.trac.wordpress.org/changeset/49334">49334</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"https://core.trac.wordpress.org/changeset/49334","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>TimothyBlynJacobs</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2020-10-27 18:30:03 +0000 (Tue, 27 Oct 2020)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>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 <a href="https://core.trac.wordpress.org/ticket/51638">#51638</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcjs_enqueuesadminsitehealthjs">trunk/src/js/_enqueues/admin/site-health.js</a></li>
<li><a href="#trunksrcwpadminincludesclasswpsitehealthphp">trunk/src/wp-admin/includes/class-wp-site-health.php</a></li>
<li><a href="#trunksrcwpincludesrestapiendpointsclasswprestsitehealthcontrollerphp">trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php</a></li>
<li><a href="#trunktestsphpunittestsrestapirestschemasetupphp">trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcjs_enqueuesadminsitehealthjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/js/_enqueues/admin/site-health.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/js/_enqueues/admin/site-health.js       2020-10-27 18:17:40 UTC (rev 49333)
+++ trunk/src/js/_enqueues/admin/site-health.js 2020-10-27 18:30:03 UTC (rev 49334)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -212,7 +212,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                if ( 'undefined' !== typeof( this.has_rest ) && this.has_rest ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                        wp.apiRequest( {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                url: this.test
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         url: this.test,
+                                               headers: this.headers
</ins><span class="cx" style="display: block; padding: 0 10px">                                         } )
</span><span class="cx" style="display: block; padding: 0 10px">                                                .done( function( response ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                                        /** This filter is documented in wp-admin/includes/class-wp-site-health.php */
</span></span></pre></div>
<a id="trunksrcwpadminincludesclasswpsitehealthphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-admin/includes/class-wp-site-health.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-admin/includes/class-wp-site-health.php      2020-10-27 18:17:40 UTC (rev 49333)
+++ trunk/src/wp-admin/includes/class-wp-site-health.php        2020-10-27 18:30:03 UTC (rev 49334)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -135,6 +135,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                'test'      => $test['test'],
</span><span class="cx" style="display: block; padding: 0 10px">                                                'has_rest'  => ( isset( $test['has_rest'] ) ? $test['has_rest'] : false ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                'completed' => false,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                'headers'   => isset( $test['headers'] ) ? $test['headers'] : array(),
</ins><span class="cx" style="display: block; padding: 0 10px">                                         );
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2079,6 +2080,62 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * 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(
+                               '<p>%s</p>',
+                               __( '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(
+                               '<p><a href="%s">%s</a></p>',
+                               esc_url( admin_url( 'options-permalink.php' ) ),
+                               __( 'Flush permalinks' )
+                       );
+               } else {
+                       $result['actions'] .= sprintf(
+                               '<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>',
+                               '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;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Return a set of tests that belong to the site status page.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * Each site status test is defined here, they may be `direct` tests, that run on page load, or `async` tests
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2177,6 +2234,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        'has_rest'          => true,
</span><span class="cx" style="display: block; padding: 0 10px">                                        'async_direct_test' => array( WP_Site_Health::get_instance(), 'get_test_loopback_requests' ),
</span><span class="cx" style="display: block; padding: 0 10px">                                ),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                '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,
+                               ),
</ins><span class="cx" style="display: block; padding: 0 10px">                         ),
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2203,6 +2267,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @since 5.2.0
</span><span class="cx" style="display: block; padding: 0 10px">                 * @since 5.6.0 Added the `async_direct_test` array key.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 *              Added the `skip_cron` array key.
</ins><span class="cx" style="display: block; padding: 0 10px">                  *
</span><span class="cx" style="display: block; padding: 0 10px">                 * @param array $test_type {
</span><span class="cx" style="display: block; padding: 0 10px">                 *     An associative array, where the `$test_type` is either `direct` or
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2217,6 +2282,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 *         @type mixed    $test              A callable to perform a direct test, or a string AJAX action
</span><span class="cx" style="display: block; padding: 0 10px">                 *                                           to be called to perform an async test.
</span><span class="cx" style="display: block; padding: 0 10px">                 *         @type boolean  $has_rest          Optional. Denote if `$test` has a REST API endpoint.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 *         @type boolean  $skip_cron         Whether to skip this test when running as cron.
</ins><span class="cx" style="display: block; padding: 0 10px">                  *         @type callable $async_direct_test A manner of directly calling the test marked as asynchronous,
</span><span class="cx" style="display: block; padding: 0 10px">                 *                                           as the scheduled event can not authenticate, and endpoints
</span><span class="cx" style="display: block; padding: 0 10px">                 *                                           may require authentication.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2557,6 +2623,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $tests['async'] as $test ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        if ( ! empty( $test['skip_cron'] ) ) {
+                               continue;
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         // Local endpoints may require authentication, so asynchronous tests can pass a direct test runner as well.
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! empty( $test['async_direct_test'] ) && is_callable( $test['async_direct_test'] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                // This test is callable, do so and continue to the next asynchronous check.
</span></span></pre></div>
<a id="trunksrcwpincludesrestapiendpointsclasswprestsitehealthcontrollerphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php 2020-10-27 18:17:40 UTC (rev 49333)
+++ trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php   2020-10-27 18:30:03 UTC (rev 49334)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -107,6 +107,25 @@
</span><span class="cx" style="display: block; padding: 0 10px">                register_rest_route(
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->namespace,
</span><span class="cx" style="display: block; padding: 0 10px">                        sprintf(
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                '/%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(
</ins><span class="cx" style="display: block; padding: 0 10px">                                 '/%s',
</span><span class="cx" style="display: block; padding: 0 10px">                                'directory-sizes'
</span><span class="cx" style="display: block; padding: 0 10px">                        ),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -178,6 +197,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * 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();
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Gets the current directory sizes for this install.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since 5.6.0
</span></span></pre></div>
<a id="trunktestsphpunittestsrestapirestschemasetupphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php  2020-10-27 18:17:40 UTC (rev 49333)
+++ trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php    2020-10-27 18:30:03 UTC (rev 49334)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -136,6 +136,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp-site-health/v1/tests/background-updates',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp-site-health/v1/tests/loopback-requests',
</span><span class="cx" style="display: block; padding: 0 10px">                        '/wp-site-health/v1/tests/dotorg-communication',
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        '/wp-site-health/v1/tests/authorization-header',
</ins><span class="cx" style="display: block; padding: 0 10px">                         '/wp-site-health/v1/directory-sizes',
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre>
</div>
</div>

</body>
</html>