|
$user_role = array_shift( $user_roles ); // Work with the copy |
I was working on a site today that would not show stats to an admin user (below) until I explicitly added
view_stats. As best I can tell, that's because the current code only considers the user's first listed role. It may be a rare scenario, but if (as I believe they are) roles are ordered by insertion date/time, in this case the user was a customer before they became an admin.
150648781@www.[redacted]wine.com:~$ wp user meta get 56531 wp_capabilities
array (
'customer' => true,
'administrator' => true,
'view_stats' => true,
)
AI generated text below here:
Summary
On a single-site WordPress install using Jetpack, a user who has the administrator role could not see Jetpack Stats until I explicitly granted the view_stats capability at the user level. This appears to be caused by Jetpack Stats’ view_stats meta-cap mapping only considering the first role in WP_User::$roles (derived from the stored wp_capabilities array order), rather than checking all roles the user has.
Expected behavior
If a user has any role that is permitted to view stats (e.g. administrator is included in the Stats roles allowlist), then current_user_can( 'view_stats' ) should succeed and the Stats UI should be available. Multi-role users should not be blocked simply because administrator is not the “first” listed role.
Suggested fix
Update the view_stats meta-cap mapping to consider all of a user’s roles, not just the first role returned by WP_User::$roles.
Today the code effectively does:
$user_role = array_shift( $user->roles );
- allow if
$user_role is in Options::get_option( 'roles' )
This breaks for multi-role users when the first role is not in the allowlist.
Proposed logic:
- Get
$user_roles = (array) $user->roles;
- Get
$stats_roles = (array) Options::get_option( 'roles' );
- If any role overlaps (
array_intersect( $user_roles, $stats_roles ) is non-empty), map view_stats to read.
Pseudo-code:
if ( 'view_stats' === $cap ) {
$user = new WP_User( $user_id );
$user_roles = (array) $user->roles;
$stats_roles = Options::get_option( 'roles' );
if ( is_array( $stats_roles ) && ! empty( array_intersect( $user_roles, $stats_roles ) ) ) {
$caps = array( 'read' );
}
}
jetpack/projects/packages/stats/src/class-main.php
Line 127 in 6127ab9
view_stats. As best I can tell, that's because the current code only considers the user's first listed role. It may be a rare scenario, but if (as I believe they are) roles are ordered by insertion date/time, in this case the user was a customer before they became an admin.AI generated text below here:
Summary
On a single-site WordPress install using Jetpack, a user who has the
administratorrole could not see Jetpack Stats until I explicitly granted theview_statscapability at the user level. This appears to be caused by Jetpack Stats’view_statsmeta-cap mapping only considering the first role inWP_User::$roles(derived from the storedwp_capabilitiesarray order), rather than checking all roles the user has.Expected behavior
If a user has any role that is permitted to view stats (e.g.
administratoris included in the Stats roles allowlist), thencurrent_user_can( 'view_stats' )should succeed and the Stats UI should be available. Multi-role users should not be blocked simply becauseadministratoris not the “first” listed role.Suggested fix
Update the
view_statsmeta-cap mapping to consider all of a user’s roles, not just the first role returned byWP_User::$roles.Today the code effectively does:
$user_role = array_shift( $user->roles );$user_roleis inOptions::get_option( 'roles' )This breaks for multi-role users when the first role is not in the allowlist.
Proposed logic:
$user_roles = (array) $user->roles;$stats_roles = (array) Options::get_option( 'roles' );array_intersect( $user_roles, $stats_roles )is non-empty), mapview_statstoread.Pseudo-code: