Make WordPress Core

Changeset 52157


Ignore:
Timestamp:
11/15/2021 01:08:35 AM (5 years ago)
Author:
TimothyBlynJacobs
Message:

Users: Prevent infinite loop when using capability checks during determine_current_user on multisite.

On multisite, when checking if a user has a certain capability WordPress makes an additional check to see if the user is a super admin. The is_super_admin() function contained a call to wp_get_current_user() so as the global current user object could be used if it matched the queried user id.

This would cause an infinite loop if a hook attached to the determine_current_user filter was itself making a permission check. For example when limiting who can use the Application Passwords feature based on their capabilities.

Since [50790] the WP_User instance for the current user is shared between wp_get_current_user() and get_userdata(). This means we can remove the wp_get_current_user call from is_super_admin() while still retaining the same behavior.

Props chrisvanpatten, peterwilsoncc.
Fixes #53386.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/capabilities.php

    r50490 r52157  
    889889 */
    890890function is_super_admin( $user_id = false ) {
    891         if ( ! $user_id || get_current_user_id() == $user_id ) {
     891        if ( ! $user_id ) {
    892892                $user = wp_get_current_user();
    893893        } else {
  • trunk/tests/phpunit/tests/auth.php

    r52010 r52157  
    636636                $this->assertNull( wp_validate_application_password( null ) );
    637637        }
     638
     639        /**
     640         * @ticket 53386
     641         * @dataProvider data_application_passwords_can_use_capability_checks_to_determine_feature_availability
     642         */
     643        public function test_application_passwords_can_use_capability_checks_to_determine_feature_availability( $role, $authenticated ) {
     644                $user = self::factory()->user->create_and_get( array( 'role' => $role ) );
     645
     646                list( $password ) = WP_Application_Passwords::create_new_application_password( $user->ID, array( 'name' => 'phpunit' ) );
     647
     648                add_filter( 'application_password_is_api_request', '__return_true' );
     649                add_filter( 'wp_is_application_passwords_available', '__return_true' );
     650                add_filter(
     651                        'wp_is_application_passwords_available_for_user',
     652                        static function ( $available, WP_User $user ) {
     653                                return user_can( $user, 'edit_posts' );
     654                        },
     655                        10,
     656                        2
     657                );
     658
     659                $_SERVER['PHP_AUTH_USER'] = $user->user_login;
     660                $_SERVER['PHP_AUTH_PW']   = $password;
     661
     662                unset( $GLOBALS['current_user'] );
     663                $current = get_current_user_id();
     664
     665                if ( $authenticated ) {
     666                        $this->assertSame( $user->ID, $current );
     667                } else {
     668                        $this->assertSame( 0, $current );
     669                }
     670        }
     671
     672        public function data_application_passwords_can_use_capability_checks_to_determine_feature_availability() {
     673                return array(
     674                        'allowed'     => array( 'editor', true ),
     675                        'not allowed' => array( 'subscriber', false ),
     676                );
     677        }
    638678}
Note: See TracChangeset for help on using the changeset viewer.