Make WordPress Core

Changeset 52157


Ignore:
Timestamp:
11/15/2021 01:08:35 AM (3 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.