From 66f7cee321a7a22a605c413f753f8fa94dc78233 Mon Sep 17 00:00:00 2001 From: Peter Wilson <peterwilsoncc@git.wordpress.org> Date: Mon, 10 Jun 2019 07:41:12 +0000 Subject: [PATCH] Site health: Introduce `view_site_health_checks` capability. Introduces the faux primitive capability `view_site_health_checks` available to single site admins and multisite super-admin to view the site health page within the admin. The capability is mapped to the `install_plugins` capability without being dependent on the file system being writable. This fixes a bug where the feature couldn't be used by sites unable to write to the file system or managed through version control. The capability is granted on the `user_has_cap` filter. Props birgire, Clorith, palmiak, peterwilsoncc, spacedmonkey. Fixes #46957. git-svn-id: https://develop.svn.wordpress.org/trunk@45507 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/includes/ajax-actions.php | 12 +- src/wp-admin/menu.php | 2 +- src/wp-admin/site-health-info.php | 2 +- src/wp-admin/site-health.php | 2 +- src/wp-includes/capabilities.php | 25 ++ src/wp-includes/default-filters.php | 1 + tests/phpunit/tests/user/capabilities.php | 273 +++++++++++----------- 7 files changed, 173 insertions(+), 144 deletions(-) diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 1056997831..6c1e404e88 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -4869,7 +4869,7 @@ function wp_ajax_wp_privacy_erase_personal_data() { function wp_ajax_health_check_dotorg_communication() { check_ajax_referer( 'health-check-site-status' ); - if ( ! current_user_can( 'install_plugins' ) ) { + if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_send_json_error(); } @@ -4889,7 +4889,7 @@ function wp_ajax_health_check_dotorg_communication() { function wp_ajax_health_check_is_in_debug_mode() { wp_verify_nonce( 'health-check-site-status' ); - if ( ! current_user_can( 'install_plugins' ) ) { + if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_send_json_error(); } @@ -4909,7 +4909,7 @@ function wp_ajax_health_check_is_in_debug_mode() { function wp_ajax_health_check_background_updates() { check_ajax_referer( 'health-check-site-status' ); - if ( ! current_user_can( 'install_plugins' ) ) { + if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_send_json_error(); } @@ -4930,7 +4930,7 @@ function wp_ajax_health_check_background_updates() { function wp_ajax_health_check_loopback_requests() { check_ajax_referer( 'health-check-site-status' ); - if ( ! current_user_can( 'install_plugins' ) ) { + if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_send_json_error(); } @@ -4950,7 +4950,7 @@ function wp_ajax_health_check_loopback_requests() { function wp_ajax_health_check_site_status_result() { check_ajax_referer( 'health-check-site-status-result' ); - if ( ! current_user_can( 'install_plugins' ) ) { + if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_send_json_error(); } @@ -4967,7 +4967,7 @@ function wp_ajax_health_check_site_status_result() { function wp_ajax_health_check_get_sizes() { check_ajax_referer( 'health-check-site-status-result' ); - if ( ! current_user_can( 'install_plugins' ) || is_multisite() ) { + if ( ! current_user_can( 'view_site_health_checks' ) || is_multisite() ) { wp_send_json_error(); } diff --git a/src/wp-admin/menu.php b/src/wp-admin/menu.php index da441f0541..96c4c2a9c4 100644 --- a/src/wp-admin/menu.php +++ b/src/wp-admin/menu.php @@ -263,7 +263,7 @@ $menu[75] = array( __( 'Tools' ), 'edit_posts', 'tools.php', $submenu['tools.php'][5] = array( __( 'Available Tools' ), 'edit_posts', 'tools.php' ); $submenu['tools.php'][10] = array( __( 'Import' ), 'import', 'import.php' ); $submenu['tools.php'][15] = array( __( 'Export' ), 'export', 'export.php' ); - $submenu['tools.php'][20] = array( __( 'Site Health' ), 'install_plugins', 'site-health.php' ); + $submenu['tools.php'][20] = array( __( 'Site Health' ), 'view_site_health_checks', 'site-health.php' ); $submenu['tools.php'][25] = array( __( 'Export Personal Data' ), 'export_others_personal_data', 'export-personal-data.php' ); $submenu['tools.php'][30] = array( __( 'Erase Personal Data' ), 'erase_others_personal_data', 'erase-personal-data.php' ); if ( is_multisite() && ! is_main_site() ) { diff --git a/src/wp-admin/site-health-info.php b/src/wp-admin/site-health-info.php index 42f2be0367..d65581d9c2 100644 --- a/src/wp-admin/site-health-info.php +++ b/src/wp-admin/site-health-info.php @@ -11,7 +11,7 @@ require_once( dirname( __FILE__ ) . '/admin.php' ); $title = __( 'Site Health Info' ); -if ( ! current_user_can( 'install_plugins' ) ) { +if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_die( __( 'Sorry, you are not allowed to access the debug data.' ), '', 403 ); } diff --git a/src/wp-admin/site-health.php b/src/wp-admin/site-health.php index 18536d9bbf..5f19ebee0d 100644 --- a/src/wp-admin/site-health.php +++ b/src/wp-admin/site-health.php @@ -16,7 +16,7 @@ require_once( dirname( __FILE__ ) . '/admin.php' ); $title = __( 'Site Health Status' ); -if ( ! current_user_can( 'install_plugins' ) ) { +if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_die( __( 'Sorry, you are not allowed to access site health information.' ), '', 403 ); } diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php index ac8aaa52d2..4fab5d3204 100644 --- a/src/wp-includes/capabilities.php +++ b/src/wp-includes/capabilities.php @@ -1025,6 +1025,31 @@ function wp_maybe_grant_resume_extensions_caps( $allcaps ) { return $allcaps; } +/** + * Filters the user capabilities to grant the 'view_site_health_checks' capabilities as necessary. + * + * @since 5.2.2 + * + * @param bool[] $allcaps An array of all the user's capabilities. + * @param string[] $caps Required primitive capabilities for the requested capability. + * @param array $args { + * Arguments that accompany the requested capability check. + * + * @type string $0 Requested capability. + * @type int $1 Concerned user ID. + * @type mixed ...$2 Optional second and further parameters, typically object ID. + * } + * @param WP_User $user The user object. + * @return bool[] Filtered array of the user's capabilities. + */ +function wp_maybe_grant_site_health_caps( $allcaps, $caps, $args, $user ) { + if ( ! empty( $allcaps['install_plugins'] ) && ( ! is_multisite() || is_super_admin( $user->ID ) ) ) { + $allcaps['view_site_health_checks'] = true; + } + + return $allcaps; +} + return; // Dummy gettext calls to get strings in the catalog. diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index ecdc87cb3a..056e8ffb43 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -580,5 +580,6 @@ add_filter( 'pre_oembed_result', 'wp_filter_pre_oembed_result', 10, 3 ); // Capabilities add_filter( 'user_has_cap', 'wp_maybe_grant_install_languages_cap', 1 ); add_filter( 'user_has_cap', 'wp_maybe_grant_resume_extensions_caps', 1 ); +add_filter( 'user_has_cap', 'wp_maybe_grant_site_health_caps', 1, 4 ); unset( $filter, $action ); diff --git a/tests/phpunit/tests/user/capabilities.php b/tests/phpunit/tests/user/capabilities.php index 86d70c3ea2..a0446f17e4 100644 --- a/tests/phpunit/tests/user/capabilities.php +++ b/tests/phpunit/tests/user/capabilities.php @@ -76,79 +76,80 @@ class Tests_User_Capabilities extends WP_UnitTestCase { final private function _getSingleSitePrimitiveCaps() { return array( - 'unfiltered_html' => array( 'administrator', 'editor' ), + 'unfiltered_html' => array( 'administrator', 'editor' ), - 'activate_plugins' => array( 'administrator' ), - 'create_users' => array( 'administrator' ), - 'delete_plugins' => array( 'administrator' ), - 'delete_themes' => array( 'administrator' ), - 'delete_users' => array( 'administrator' ), - 'edit_files' => array( 'administrator' ), - 'edit_plugins' => array( 'administrator' ), - 'edit_themes' => array( 'administrator' ), - 'edit_users' => array( 'administrator' ), - 'install_plugins' => array( 'administrator' ), - 'install_themes' => array( 'administrator' ), - 'update_core' => array( 'administrator' ), - 'update_plugins' => array( 'administrator' ), - 'update_themes' => array( 'administrator' ), - 'edit_theme_options' => array( 'administrator' ), - 'export' => array( 'administrator' ), - 'import' => array( 'administrator' ), - 'list_users' => array( 'administrator' ), - 'manage_options' => array( 'administrator' ), - 'promote_users' => array( 'administrator' ), - 'remove_users' => array( 'administrator' ), - 'switch_themes' => array( 'administrator' ), - 'edit_dashboard' => array( 'administrator' ), - 'resume_plugins' => array( 'administrator' ), - 'resume_themes' => array( 'administrator' ), + 'activate_plugins' => array( 'administrator' ), + 'create_users' => array( 'administrator' ), + 'delete_plugins' => array( 'administrator' ), + 'delete_themes' => array( 'administrator' ), + 'delete_users' => array( 'administrator' ), + 'edit_files' => array( 'administrator' ), + 'edit_plugins' => array( 'administrator' ), + 'edit_themes' => array( 'administrator' ), + 'edit_users' => array( 'administrator' ), + 'install_plugins' => array( 'administrator' ), + 'install_themes' => array( 'administrator' ), + 'update_core' => array( 'administrator' ), + 'update_plugins' => array( 'administrator' ), + 'update_themes' => array( 'administrator' ), + 'edit_theme_options' => array( 'administrator' ), + 'export' => array( 'administrator' ), + 'import' => array( 'administrator' ), + 'list_users' => array( 'administrator' ), + 'manage_options' => array( 'administrator' ), + 'promote_users' => array( 'administrator' ), + 'remove_users' => array( 'administrator' ), + 'switch_themes' => array( 'administrator' ), + 'edit_dashboard' => array( 'administrator' ), + 'resume_plugins' => array( 'administrator' ), + 'resume_themes' => array( 'administrator' ), + 'view_site_health_checks' => array( 'administrator' ), - 'moderate_comments' => array( 'administrator', 'editor' ), - 'manage_categories' => array( 'administrator', 'editor' ), - 'edit_others_posts' => array( 'administrator', 'editor' ), - 'edit_pages' => array( 'administrator', 'editor' ), - 'edit_others_pages' => array( 'administrator', 'editor' ), - 'edit_published_pages' => array( 'administrator', 'editor' ), - 'publish_pages' => array( 'administrator', 'editor' ), - 'delete_pages' => array( 'administrator', 'editor' ), - 'delete_others_pages' => array( 'administrator', 'editor' ), - 'delete_published_pages' => array( 'administrator', 'editor' ), - 'delete_others_posts' => array( 'administrator', 'editor' ), - 'delete_private_posts' => array( 'administrator', 'editor' ), - 'edit_private_posts' => array( 'administrator', 'editor' ), - 'read_private_posts' => array( 'administrator', 'editor' ), - 'delete_private_pages' => array( 'administrator', 'editor' ), - 'edit_private_pages' => array( 'administrator', 'editor' ), - 'read_private_pages' => array( 'administrator', 'editor' ), + 'moderate_comments' => array( 'administrator', 'editor' ), + 'manage_categories' => array( 'administrator', 'editor' ), + 'edit_others_posts' => array( 'administrator', 'editor' ), + 'edit_pages' => array( 'administrator', 'editor' ), + 'edit_others_pages' => array( 'administrator', 'editor' ), + 'edit_published_pages' => array( 'administrator', 'editor' ), + 'publish_pages' => array( 'administrator', 'editor' ), + 'delete_pages' => array( 'administrator', 'editor' ), + 'delete_others_pages' => array( 'administrator', 'editor' ), + 'delete_published_pages' => array( 'administrator', 'editor' ), + 'delete_others_posts' => array( 'administrator', 'editor' ), + 'delete_private_posts' => array( 'administrator', 'editor' ), + 'edit_private_posts' => array( 'administrator', 'editor' ), + 'read_private_posts' => array( 'administrator', 'editor' ), + 'delete_private_pages' => array( 'administrator', 'editor' ), + 'edit_private_pages' => array( 'administrator', 'editor' ), + 'read_private_pages' => array( 'administrator', 'editor' ), - 'edit_published_posts' => array( 'administrator', 'editor', 'author' ), - 'upload_files' => array( 'administrator', 'editor', 'author' ), - 'publish_posts' => array( 'administrator', 'editor', 'author' ), - 'delete_published_posts' => array( 'administrator', 'editor', 'author' ), + 'edit_published_posts' => array( 'administrator', 'editor', 'author' ), + 'upload_files' => array( 'administrator', 'editor', 'author' ), + 'publish_posts' => array( 'administrator', 'editor', 'author' ), + 'delete_published_posts' => array( 'administrator', 'editor', 'author' ), - 'edit_posts' => array( 'administrator', 'editor', 'author', 'contributor' ), - 'delete_posts' => array( 'administrator', 'editor', 'author', 'contributor' ), + 'edit_posts' => array( 'administrator', 'editor', 'author', 'contributor' ), + 'delete_posts' => array( 'administrator', 'editor', 'author', 'contributor' ), - 'read' => array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ), + 'read' => array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ), - 'level_10' => array( 'administrator' ), - 'level_9' => array( 'administrator' ), - 'level_8' => array( 'administrator' ), - 'level_7' => array( 'administrator', 'editor' ), - 'level_6' => array( 'administrator', 'editor' ), - 'level_5' => array( 'administrator', 'editor' ), - 'level_4' => array( 'administrator', 'editor' ), - 'level_3' => array( 'administrator', 'editor' ), - 'level_2' => array( 'administrator', 'editor', 'author' ), - 'level_1' => array( 'administrator', 'editor', 'author', 'contributor' ), - 'level_0' => array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ), + 'level_10' => array( 'administrator' ), + 'level_9' => array( 'administrator' ), + 'level_8' => array( 'administrator' ), + 'level_7' => array( 'administrator', 'editor' ), + 'level_6' => array( 'administrator', 'editor' ), + 'level_5' => array( 'administrator', 'editor' ), + 'level_4' => array( 'administrator', 'editor' ), + 'level_3' => array( 'administrator', 'editor' ), + 'level_2' => array( 'administrator', 'editor', 'author' ), + 'level_1' => array( 'administrator', 'editor', 'author', 'contributor' ), + 'level_0' => array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ), - 'administrator' => array( 'administrator' ), - 'editor' => array( 'editor' ), - 'author' => array( 'author' ), - 'contributor' => array( 'contributor' ), - 'subscriber' => array( 'subscriber' ), + 'administrator' => array( 'administrator' ), + 'editor' => array( 'editor' ), + 'author' => array( 'author' ), + 'contributor' => array( 'contributor' ), + 'subscriber' => array( 'subscriber' ), ); @@ -157,80 +158,81 @@ class Tests_User_Capabilities extends WP_UnitTestCase { final private function _getMultiSitePrimitiveCaps() { return array( - 'unfiltered_html' => array(), + 'unfiltered_html' => array(), - 'activate_plugins' => array(), - 'create_users' => array(), - 'delete_plugins' => array(), - 'delete_themes' => array(), - 'delete_users' => array(), - 'edit_files' => array(), - 'edit_plugins' => array(), - 'edit_themes' => array(), - 'edit_users' => array(), - 'install_plugins' => array(), - 'install_themes' => array(), - 'update_core' => array(), - 'update_plugins' => array(), - 'update_themes' => array(), + 'activate_plugins' => array(), + 'create_users' => array(), + 'delete_plugins' => array(), + 'delete_themes' => array(), + 'delete_users' => array(), + 'edit_files' => array(), + 'edit_plugins' => array(), + 'edit_themes' => array(), + 'edit_users' => array(), + 'install_plugins' => array(), + 'install_themes' => array(), + 'update_core' => array(), + 'update_plugins' => array(), + 'update_themes' => array(), + 'view_site_health_checks' => array(), - 'edit_theme_options' => array( 'administrator' ), - 'export' => array( 'administrator' ), - 'import' => array( 'administrator' ), - 'list_users' => array( 'administrator' ), - 'manage_options' => array( 'administrator' ), - 'promote_users' => array( 'administrator' ), - 'remove_users' => array( 'administrator' ), - 'switch_themes' => array( 'administrator' ), - 'edit_dashboard' => array( 'administrator' ), - 'resume_plugins' => array( 'administrator' ), - 'resume_themes' => array( 'administrator' ), + 'edit_theme_options' => array( 'administrator' ), + 'export' => array( 'administrator' ), + 'import' => array( 'administrator' ), + 'list_users' => array( 'administrator' ), + 'manage_options' => array( 'administrator' ), + 'promote_users' => array( 'administrator' ), + 'remove_users' => array( 'administrator' ), + 'switch_themes' => array( 'administrator' ), + 'edit_dashboard' => array( 'administrator' ), + 'resume_plugins' => array( 'administrator' ), + 'resume_themes' => array( 'administrator' ), - 'moderate_comments' => array( 'administrator', 'editor' ), - 'manage_categories' => array( 'administrator', 'editor' ), - 'edit_others_posts' => array( 'administrator', 'editor' ), - 'edit_pages' => array( 'administrator', 'editor' ), - 'edit_others_pages' => array( 'administrator', 'editor' ), - 'edit_published_pages' => array( 'administrator', 'editor' ), - 'publish_pages' => array( 'administrator', 'editor' ), - 'delete_pages' => array( 'administrator', 'editor' ), - 'delete_others_pages' => array( 'administrator', 'editor' ), - 'delete_published_pages' => array( 'administrator', 'editor' ), - 'delete_others_posts' => array( 'administrator', 'editor' ), - 'delete_private_posts' => array( 'administrator', 'editor' ), - 'edit_private_posts' => array( 'administrator', 'editor' ), - 'read_private_posts' => array( 'administrator', 'editor' ), - 'delete_private_pages' => array( 'administrator', 'editor' ), - 'edit_private_pages' => array( 'administrator', 'editor' ), - 'read_private_pages' => array( 'administrator', 'editor' ), + 'moderate_comments' => array( 'administrator', 'editor' ), + 'manage_categories' => array( 'administrator', 'editor' ), + 'edit_others_posts' => array( 'administrator', 'editor' ), + 'edit_pages' => array( 'administrator', 'editor' ), + 'edit_others_pages' => array( 'administrator', 'editor' ), + 'edit_published_pages' => array( 'administrator', 'editor' ), + 'publish_pages' => array( 'administrator', 'editor' ), + 'delete_pages' => array( 'administrator', 'editor' ), + 'delete_others_pages' => array( 'administrator', 'editor' ), + 'delete_published_pages' => array( 'administrator', 'editor' ), + 'delete_others_posts' => array( 'administrator', 'editor' ), + 'delete_private_posts' => array( 'administrator', 'editor' ), + 'edit_private_posts' => array( 'administrator', 'editor' ), + 'read_private_posts' => array( 'administrator', 'editor' ), + 'delete_private_pages' => array( 'administrator', 'editor' ), + 'edit_private_pages' => array( 'administrator', 'editor' ), + 'read_private_pages' => array( 'administrator', 'editor' ), - 'edit_published_posts' => array( 'administrator', 'editor', 'author' ), - 'upload_files' => array( 'administrator', 'editor', 'author' ), - 'publish_posts' => array( 'administrator', 'editor', 'author' ), - 'delete_published_posts' => array( 'administrator', 'editor', 'author' ), + 'edit_published_posts' => array( 'administrator', 'editor', 'author' ), + 'upload_files' => array( 'administrator', 'editor', 'author' ), + 'publish_posts' => array( 'administrator', 'editor', 'author' ), + 'delete_published_posts' => array( 'administrator', 'editor', 'author' ), - 'edit_posts' => array( 'administrator', 'editor', 'author', 'contributor' ), - 'delete_posts' => array( 'administrator', 'editor', 'author', 'contributor' ), + 'edit_posts' => array( 'administrator', 'editor', 'author', 'contributor' ), + 'delete_posts' => array( 'administrator', 'editor', 'author', 'contributor' ), - 'read' => array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ), + 'read' => array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ), - 'level_10' => array( 'administrator' ), - 'level_9' => array( 'administrator' ), - 'level_8' => array( 'administrator' ), - 'level_7' => array( 'administrator', 'editor' ), - 'level_6' => array( 'administrator', 'editor' ), - 'level_5' => array( 'administrator', 'editor' ), - 'level_4' => array( 'administrator', 'editor' ), - 'level_3' => array( 'administrator', 'editor' ), - 'level_2' => array( 'administrator', 'editor', 'author' ), - 'level_1' => array( 'administrator', 'editor', 'author', 'contributor' ), - 'level_0' => array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ), + 'level_10' => array( 'administrator' ), + 'level_9' => array( 'administrator' ), + 'level_8' => array( 'administrator' ), + 'level_7' => array( 'administrator', 'editor' ), + 'level_6' => array( 'administrator', 'editor' ), + 'level_5' => array( 'administrator', 'editor' ), + 'level_4' => array( 'administrator', 'editor' ), + 'level_3' => array( 'administrator', 'editor' ), + 'level_2' => array( 'administrator', 'editor', 'author' ), + 'level_1' => array( 'administrator', 'editor', 'author', 'contributor' ), + 'level_0' => array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ), - 'administrator' => array( 'administrator' ), - 'editor' => array( 'editor' ), - 'author' => array( 'author' ), - 'contributor' => array( 'contributor' ), - 'subscriber' => array( 'subscriber' ), + 'administrator' => array( 'administrator' ), + 'editor' => array( 'editor' ), + 'author' => array( 'author' ), + 'contributor' => array( 'contributor' ), + 'subscriber' => array( 'subscriber' ), ); @@ -397,9 +399,10 @@ class Tests_User_Capabilities extends WP_UnitTestCase { $actual['author'], $actual['subscriber'], $actual['contributor'], - // the following two are granted via `user_has_cap`: + // The following are granted via `user_has_cap`: $actual['resume_plugins'], - $actual['resume_themes'] + $actual['resume_themes'], + $actual['view_site_health_checks'] ); unset( @@ -1200,7 +1203,7 @@ class Tests_User_Capabilities extends WP_UnitTestCase { $this->assertFalse( $contributor->has_cap( 'publish_post', $post ) ); $this->assertFalse( $contributor->has_cap( 'edit_post', $post ) ); $this->assertFalse( $contributor->has_cap( 'delete_post', $post ) ); - $this->assertEquals( $status === 'publish', $contributor->has_cap( 'read_post', $post ) ); + $this->assertEquals( 'publish' === $status, $contributor->has_cap( 'read_post', $post ) ); } /**