Make WordPress Core

Opened 22 months ago

Last modified 22 months ago

#56256 new defect (bug)

The 'capability' parameter in the get_users function may not give the expected results.

Reported by: tmatsuur's profile tmatsuur Owned by:
Milestone: Future Release Priority: normal
Severity: normal Version: 5.9
Component: Role/Capability Keywords: has-patch has-unit-tests
Focuses: Cc:

Description

Specifying multiple different capabilities for the 'capability', 'capabilityin', and 'capabilitynot_in' parameters of the get_users function may not produce the expected results.

The user's metadata is as follows

mysql> select * from wp_usermeta where meta_key='wp_capabilities';
+----------+---------+-----------------+---------------------------------+
| umeta_id | user_id | meta_key        | meta_value                      |
+----------+---------+-----------------+---------------------------------+
|       12 |       1 | wp_capabilities | a:1:{s:13:"administrator";b:1;} |
|       54 |       2 | wp_capabilities | a:1:{s:6:"author";b:1;}         |
+----------+---------+-----------------+---------------------------------+

In this situation, run the following PHP script

var_dump( get_users( [ 'capability' => ['edit_posts', 'edit_pages'], 'fields' => 'ID' ] ) );

The output is as follows, and no matching user ID can be obtained.

array(0) {
}

The cause is in the code after line 481 of class-wp-user-query.php.

		foreach ( $available_roles as $role => $role_data ) {
			$role_caps = array_keys( array_filter( $role_data['capabilities'] ) );

			foreach ( $capabilities as $cap ) {
				if ( in_array( $cap, $role_caps, true ) ) {
					$caps_with_roles[ $cap ][] = $role;
					break;
				}
			}

			foreach ( $capability__in as $cap ) {
				if ( in_array( $cap, $role_caps, true ) ) {
					$role__in[] = $role;
					break;
				}
			}

			foreach ( $capability__not_in as $cap ) {
				if ( in_array( $cap, $role_caps, true ) ) {
					$role__not_in[] = $role;
					break;
				}
			}
		}

Because the loop process is terminated immediately after storing the "role" of the first "capability" in the array, the "roles" of the second and subsequent "capabilities" are not stored.

Therefore, the 'role' of the second and subsequent 'capability' is no longer included in the search criteria, and the expected results are not obtained.

Specifically, the search query looks like this

				WHERE 1=1 AND ( 
  ( 
    ( 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"edit\\_posts\"%' ) 
      OR 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"administrator\"%' ) 
      OR 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"editor\"%' ) 
      OR 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"author\"%' ) 
      OR 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"contributor\"%' ) 
    ) 
    AND 
    ( 
      ( mt1.meta_key = 'wp_capabilities' AND mt1.meta_value LIKE '%\"edit\\_pages\"%' )
    )
  )
)

Remove the break statement for confirmation.

The search query then changes as follows

				WHERE 1=1 AND ( 
  ( 
    ( 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"edit\\_posts\"%' ) 
      OR 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"administrator\"%' ) 
      OR 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"editor\"%' ) 
      OR 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"author\"%' ) 
      OR 
      ( wp_usermeta.meta_key = 'wp_capabilities' AND wp_usermeta.meta_value LIKE '%\"contributor\"%' ) 
    ) 
    AND 
    ( 
      ( mt1.meta_key = 'wp_capabilities' AND mt1.meta_value LIKE '%\"edit\\_pages\"%' ) 
      OR 
      ( mt1.meta_key = 'wp_capabilities' AND mt1.meta_value LIKE '%\"administrator\"%' ) 
      OR 
      ( mt1.meta_key = 'wp_capabilities' AND mt1.meta_value LIKE '%\"editor\"%' ) 
    )
  )
)

The return value of the get_users function is as follows

array(1) {
  [0]=>
  string(1) "1"
}

Something similar occurs with the 'capabilityin' and 'capabilitynot_in' parameters.

Attachments (2)

56256.diff (719 bytes) - added by tmatsuur 22 months ago.
56256.test.diff (947 bytes) - added by johnregan3 22 months ago.
Unit Test for 56256

Download all attachments as: .zip

Change History (5)

@tmatsuur
22 months ago

#1 @swissspidy
22 months ago

  • Keywords needs-unit-tests added

Thanks for your report!

Introduced in #16841 / [51943] and [52290]

#2 @swissspidy
22 months ago

  • Component changed from Users to Role/Capability
  • Milestone changed from Awaiting Review to Future Release
  • Version changed from 6.0 to 5.9

@johnregan3
22 months ago

Unit Test for 56256

#3 @johnregan3
22 months ago

  • Keywords has-unit-tests added; needs-unit-tests removed
Note: See TracTickets for help on using tickets.