diff --git a/classes/class-admin.php b/classes/class-admin.php index 51848a38f..ea2b0f2b1 100644 --- a/classes/class-admin.php +++ b/classes/class-admin.php @@ -1748,16 +1748,20 @@ public function register_list_table() { /** * Check if a particular role has access * + * The user_has_cap/role_has_cap filters that call this are registered in the + * constructor, but the Settings object is not constructed until init priority 9. + * A capability check fired before then (e.g. by a security plugin evaluating + * firewall rules on plugins_loaded) must be denied rather than fatal on the + * null options chain. + * * @param string $role User role. * * @return bool */ private function role_can_view( $role ) { - if ( in_array( $role, $this->plugin->settings->options['general_role_access'], true ) ) { - return true; - } + $allowed_roles = $this->plugin->settings->options['general_role_access'] ?? array(); - return false; + return in_array( $role, (array) $allowed_roles, true ); } /** diff --git a/tests/phpunit/test-class-admin.php b/tests/phpunit/test-class-admin.php index ba29f6869..b16fee4b4 100644 --- a/tests/phpunit/test-class-admin.php +++ b/tests/phpunit/test-class-admin.php @@ -73,6 +73,45 @@ public function test_init() { $this->assertInstanceOf( '\WP_Stream\Export', $this->admin->export ); } + /** + * The user_has_cap filter is registered in the Admin constructor, but the + * Settings object is only built on init priority 9. A capability check for + * the view cap fired before then (e.g. a firewall plugin on plugins_loaded) + * must be denied, not fatal on the null options chain. + */ + public function test_filter_user_caps_before_settings_initialized() { + $settings = $this->plugin->settings; + $this->plugin->settings = null; + + $user = get_user_by( 'id', $this->admin_user_id ); + $allcaps = $this->admin->filter_user_caps( + array(), + array( $this->admin->view_cap ), + array( $this->admin->view_cap, $this->admin_user_id ), + $user + ); + + $this->plugin->settings = $settings; + + $this->assertArrayNotHasKey( $this->admin->view_cap, $allcaps ); + } + + /** + * Once Settings exists, the view cap is granted to allowed roles as before. + */ + public function test_filter_user_caps_grants_view_cap_to_allowed_role() { + $user = get_user_by( 'id', $this->admin_user_id ); + $allcaps = $this->admin->filter_user_caps( + array(), + array( $this->admin->view_cap ), + array( $this->admin->view_cap, $this->admin_user_id ), + $user + ); + + $this->assertArrayHasKey( $this->admin->view_cap, $allcaps ); + $this->assertTrue( $allcaps[ $this->admin->view_cap ] ); + } + public function test_prepare_admin_notices() { // Test no notices $this->admin->notices = array();