Make WordPress Core


Ignore:
Timestamp:
09/27/2017 09:09:11 PM (7 years ago)
Author:
flixos90
Message:

Multisite: Initialize a user's roles correctly when setting them up for a different site.

While it has always been possible to initialize a user's roles and capabilities for another site than the current one in a multisite, the actual roles available were not switched prior to this change, possibly causing invalid roles to show up or actually valid capabilities not being available.

In order to fix this bug in a clean way, relevant parts of the WP_User class have been refactored. The ID of the site for which capabilities are currently initialized are now stored in a private property WP_User::$site_id. The WP_User::for_blog( $blog_id ) and WP_User::_init_caps( $cap_key ) methods have been deprecated in favor of WP_User::for_site( $site_id ). In addition, a new method WP_User::get_site_id() has been introduced to retrieve the site ID for which the user's capabilities are currently initialized.

Props ryanduff, jeremyfelt, flixos90.
Fixes #36961.

File:
1 edited

Legend:

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

    r41376 r41624  
    9494
    9595    /**
     96     * The site ID the capabilities of this user are initialized for.
     97     *
     98     * @since 4.9.0
     99     * @var int
     100     */
     101    private $site_id = 0;
     102
     103    /**
    96104     * @static
    97105     * @since 3.3.0
     
    111119     * @param int|string|stdClass|WP_User $id User's ID, a WP_User object, or a user object from the DB.
    112120     * @param string $name Optional. User's username
    113      * @param int $blog_id Optional Site ID, defaults to current site.
    114      */
    115     public function __construct( $id = 0, $name = '', $blog_id = '' ) {
     121     * @param int $site_id Optional Site ID, defaults to current site.
     122     */
     123    public function __construct( $id = 0, $name = '', $site_id = '' ) {
    116124        if ( ! isset( self::$back_compat_keys ) ) {
    117125            $prefix = $GLOBALS['wpdb']->prefix;
     
    127135
    128136        if ( $id instanceof WP_User ) {
    129             $this->init( $id->data, $blog_id );
     137            $this->init( $id->data, $site_id );
    130138            return;
    131139        } elseif ( is_object( $id ) ) {
    132             $this->init( $id, $blog_id );
     140            $this->init( $id, $site_id );
    133141            return;
    134142        }
     
    146154
    147155        if ( $data ) {
    148             $this->init( $data, $blog_id );
     156            $this->init( $data, $site_id );
    149157        } else {
    150158            $this->data = new stdClass;
     
    158166     *
    159167     * @param object $data    User DB row object.
    160      * @param int    $blog_id Optional. The site ID to initialize for.
    161      */
    162     public function init( $data, $blog_id = '' ) {
     168     * @param int    $site_id Optional. The site ID to initialize for.
     169     */
     170    public function init( $data, $site_id = '' ) {
    163171        $this->data = $data;
    164172        $this->ID = (int) $data->ID;
    165173
    166         $this->for_blog( $blog_id );
     174        $this->for_site( $site_id );
    167175    }
    168176
     
    242250
    243251    /**
    244      * Makes private/protected methods readable for backward compatibility.
    245      *
    246      * @since 4.3.0
    247      *
    248      * @param callable $name      Method to call.
    249      * @param array    $arguments Arguments to pass when calling.
    250      * @return mixed|false Return value of the callback, false otherwise.
    251      */
    252     public function __call( $name, $arguments ) {
    253         if ( '_init_caps' === $name ) {
    254             return call_user_func_array( array( $this, $name ), $arguments );
    255         }
    256         return false;
    257     }
    258 
    259     /**
    260252     * Magic method for checking the existence of a certain custom field.
    261253     *
     
    426418
    427419    /**
     420     * Makes private/protected methods readable for backward compatibility.
     421     *
     422     * @since 4.3.0
     423     *
     424     * @param callable $name      Method to call.
     425     * @param array    $arguments Arguments to pass when calling.
     426     * @return mixed|false Return value of the callback, false otherwise.
     427     */
     428    public function __call( $name, $arguments ) {
     429        if ( '_init_caps' === $name ) {
     430            return call_user_func_array( array( $this, $name ), $arguments );
     431        }
     432        return false;
     433    }
     434
     435    /**
    428436     * Set up capability object properties.
    429437     *
     
    434442     *
    435443     * @since 2.1.0
     444     * @deprecated 4.9.0 Use WP_User::for_site()
    436445     *
    437446     * @global wpdb $wpdb WordPress database abstraction object.
     
    442451        global $wpdb;
    443452
    444         if ( empty($cap_key) )
    445             $this->cap_key = $wpdb->get_blog_prefix() . 'capabilities';
    446         else
     453        _deprecated_function( __METHOD__, '4.9.0', 'WP_User::for_site()' );
     454
     455        if ( empty( $cap_key ) ) {
     456            $this->cap_key = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities';
     457        } else {
    447458            $this->cap_key = $cap_key;
    448 
    449         $this->caps = get_user_meta( $this->ID, $this->cap_key, true );
    450 
    451         if ( ! is_array( $this->caps ) )
    452             $this->caps = array();
     459        }
     460
     461        $this->caps = $this->get_caps_data();
    453462
    454463        $this->get_role_caps();
     
    468477     */
    469478    public function get_role_caps() {
     479        $switch_site = false;
     480        if ( is_multisite() && $this->site_id != get_current_blog_id() ) {
     481            $switch_site = true;
     482
     483            switch_to_blog( $this->site_id );
     484        }
     485
    470486        $wp_roles = wp_roles();
    471487
     
    481497        }
    482498        $this->allcaps = array_merge( (array) $this->allcaps, (array) $this->caps );
     499
     500        if ( $switch_site ) {
     501            restore_current_blog();
     502        }
    483503
    484504        return $this->allcaps;
     
    755775     *
    756776     * @since 3.0.0
     777     * @deprecated 4.9.0 Use WP_User::for_site()
    757778     *
    758779     * @global wpdb $wpdb WordPress database abstraction object.
     
    761782     */
    762783    public function for_blog( $blog_id = '' ) {
     784        _deprecated_function( __METHOD__, '4.9.0', 'WP_User::for_site()' );
     785
     786        $this->for_site( $blog_id );
     787    }
     788
     789    /**
     790     * Sets the site to operate on. Defaults to the current site.
     791     *
     792     * @since 4.9.0
     793     *
     794     * @global wpdb $wpdb WordPress database abstraction object.
     795     *
     796     * @param int $site_id Site ID to initialize user capabilities for. Default is the current site.
     797     */
     798    public function for_site( $site_id = '' ) {
    763799        global $wpdb;
    764         if ( ! empty( $blog_id ) )
    765             $cap_key = $wpdb->get_blog_prefix( $blog_id ) . 'capabilities';
    766         else
    767             $cap_key = '';
    768         $this->_init_caps( $cap_key );
     800
     801        if ( ! empty( $site_id ) ) {
     802            $this->site_id = absint( $site_id );
     803        } else {
     804            $this->site_id = get_current_blog_id();
     805        }
     806
     807        $this->cap_key = $wpdb->get_blog_prefix( $this->site_id ) . 'capabilities';
     808
     809        $this->caps = $this->get_caps_data();
     810
     811        $this->get_role_caps();
     812    }
     813
     814    /**
     815     * Gets the ID of the site for which the user's capabilities are currently initialized.
     816     *
     817     * @since 4.9.0
     818     *
     819     * @return int Site ID.
     820     */
     821    public function get_site_id() {
     822        return $this->site_id;
     823    }
     824
     825    /**
     826     * Gets the available user capabilities data.
     827     *
     828     * @since 4.9.0
     829     *
     830     * @return array User capabilities array.
     831     */
     832    private function get_caps_data() {
     833        $caps = get_user_meta( $this->ID, $this->cap_key, true );
     834
     835        if ( ! is_array( $caps ) ) {
     836            return array();
     837        }
     838
     839        return $caps;
    769840    }
    770841}
Note: See TracChangeset for help on using the changeset viewer.