Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/wp-includes/capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,30 @@ function map_meta_cap( $cap, $user_id, ...$args ) {
// In multisite the user must be a super admin to remove themselves.
if ( isset( $args[0] ) && $user_id === (int) $args[0] && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow';
} elseif (
isset( $args[0] )
&& user_can( (int) $args[0], 'self_protect' )
&& ! user_can( $user_id, 'manage_self_protected_users' )
&& ! user_can( $user_id, 'self_protect' )
) {
$caps[] = 'do_not_allow';
} else {
$caps[] = 'remove_users';
}
break;
case 'promote_user':
if (
isset( $args[0] )
&& user_can( (int) $args[0], 'self_protect' )
&& ! user_can( $user_id, 'manage_self_protected_users' )
&& ! user_can( $user_id, 'self_protect' )
) {
$caps[] = 'do_not_allow';
break;
}

$caps[] = 'promote_users';
break;
case 'add_users':
$caps[] = 'promote_users';
break;
Expand All @@ -71,6 +90,17 @@ function map_meta_cap( $cap, $user_id, ...$args ) {
break;
}

if (
'edit_user' === $cap
&& isset( $args[0] )
&& user_can( (int) $args[0], 'self_protect' )
&& ! user_can( $user_id, 'manage_self_protected_users' )
&& ! user_can( $user_id, 'self_protect' )
) {
$caps[] = 'do_not_allow';
break;
}

// In multisite the user must have manage_network_users caps. If editing a super admin, the user must be a super admin.
if ( is_multisite() && ( ( ! is_super_admin( $user_id ) && 'edit_user' === $cap && is_super_admin( $args[0] ) ) || ! user_can( $user_id, 'manage_network_users' ) ) ) {
$caps[] = 'do_not_allow';
Expand Down Expand Up @@ -672,6 +702,17 @@ function map_meta_cap( $cap, $user_id, ...$args ) {
break;
case 'delete_user':
case 'delete_users':
if (
'delete_user' === $cap
&& isset( $args[0] )
&& user_can( (int) $args[0], 'self_protect' )
&& ! user_can( $user_id, 'manage_self_protected_users' )
&& ! user_can( $user_id, 'self_protect' )
) {
$caps[] = 'do_not_allow';
break;
}

// If multisite only super admins can delete users.
if ( is_multisite() && ! is_super_admin( $user_id ) ) {
$caps[] = 'do_not_allow';
Expand Down
67 changes: 67 additions & 0 deletions tests/phpunit/tests/user/capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -1911,6 +1911,73 @@ public function test_only_admins_can_delete_users_on_single_site() {
$this->assertFalse( user_can( self::$users['subscriber']->ID, 'delete_user', self::$users['subscriber']->ID ) );
}

/**
* @ticket 14460
*
* @group ms-excluded
*/
public function test_user_cannot_manage_self_protected_user_without_override_cap() {
$admin = self::$users['administrator'];
$target = self::$users['subscriber'];
$caps = array( 'edit_user', 'delete_user', 'promote_user', 'remove_user' );

$target->add_cap( 'self_protect' );

try {
foreach ( $caps as $cap ) {
$this->assertFalse( user_can( $admin->ID, $cap, $target->ID ), "User should not have the {$cap} capability for a self-protected user." );
}

$admin->add_cap( 'self_protect' );

foreach ( $caps as $cap ) {
$this->assertTrue( user_can( $admin->ID, $cap, $target->ID ), "User should have the {$cap} capability for a self-protected user when they are also self-protected." );
}

$admin->remove_cap( 'self_protect' );
$admin->add_cap( 'manage_self_protected_users' );

foreach ( $caps as $cap ) {
$this->assertTrue( user_can( $admin->ID, $cap, $target->ID ), "User should have the {$cap} capability for a self-protected user when they have the override capability." );
}
} finally {
$admin->remove_cap( 'manage_self_protected_users' );
$admin->remove_cap( 'self_protect' );
$target->remove_cap( 'self_protect' );
}
}

/**
* @ticket 14460
*
* @group ms-required
*/
public function test_multisite_admin_with_manage_network_users_cannot_edit_self_protected_user() {
$admin = self::$users['administrator'];
$target = self::$users['subscriber'];

$admin->add_cap( 'manage_network_users' );
$target->add_cap( 'self_protect' );

try {
$this->assertFalse( user_can( $admin->ID, 'edit_user', $target->ID ) );

$admin->add_cap( 'self_protect' );

$this->assertTrue( user_can( $admin->ID, 'edit_user', $target->ID ) );

$admin->remove_cap( 'self_protect' );
$admin->add_cap( 'manage_self_protected_users' );

$this->assertTrue( user_can( $admin->ID, 'edit_user', $target->ID ) );
} finally {
$admin->remove_cap( 'manage_self_protected_users' );
$admin->remove_cap( 'self_protect' );
$admin->remove_cap( 'manage_network_users' );
$target->remove_cap( 'self_protect' );
}
}

public function test_only_admins_and_super_admins_can_promote_users() {
if ( is_multisite() ) {
$this->assertTrue( user_can( self::$super_admin->ID, 'promote_user', self::$users['subscriber']->ID ) );
Expand Down
Loading