-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Prevent modifications of the VIP machine user (#1206)
It should never be editable/removable.
- Loading branch information
Showing
4 changed files
with
216 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
namespace Automattic\VIP\Security; | ||
|
||
const USER_MODIFICATION_CAPS = [ | ||
'edit_user', | ||
'delete_user', | ||
'remove_user', | ||
'promote_user', | ||
]; | ||
|
||
// Don't allow the VIP machine user to be edited or removed. | ||
function prevent_machine_user_mods( $caps, $requested_cap, $user_id, $args ) { | ||
if ( in_array( $requested_cap, USER_MODIFICATION_CAPS, true ) ) { | ||
// args[0] is who we're performing the action on. | ||
$user_to_edit_id = $args[ 0 ]; | ||
$user_to_edit = get_userdata( $user_to_edit_id ); | ||
|
||
if ( WPCOM_VIP_MACHINE_USER_LOGIN === $user_to_edit->user_login ) { | ||
return [ 'do_not_allow' ]; | ||
} | ||
} | ||
|
||
return $caps; | ||
} | ||
// Use map_meta_cap instead of user_has_cap so we can restrict superadmins as well. | ||
// WP_User::has_cap runs the user_has_cap filter after the superadmin check, which makes it impossible to use for restricting them. | ||
add_filter( 'map_meta_cap', __NAMESPACE__ . '\prevent_machine_user_mods', PHP_INT_MAX, 4 ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
<?php | ||
|
||
namespace Automattic\VIP\Security; | ||
|
||
class Machine_User_Test extends \WP_UnitTestCase { | ||
|
||
public static function setUpBeforeClass() { | ||
parent::setUpBeforeClass(); | ||
|
||
require_once __DIR__ . '/../../security/machine-user.php'; | ||
} | ||
|
||
public function setUp() { | ||
parent::setUp(); | ||
|
||
$this->machine_user = $this->factory->user->create_and_get( [ | ||
'user_login' => WPCOM_VIP_MACHINE_USER_LOGIN, | ||
'user_email' => WPCOM_VIP_MACHINE_USER_EMAIL, | ||
'role' => WPCOM_VIP_MACHINE_USER_ROLE, | ||
] ); | ||
|
||
} | ||
|
||
public function get_test_data__user_modification_caps() { | ||
return [ | ||
'edit_user' => [ 'edit_user' ], | ||
'remove_user' => [ 'remove_user' ], | ||
'delete_user' => [ 'delete_user' ], | ||
'promote_user' => [ 'promote_user' ], | ||
]; | ||
} | ||
|
||
// For testing non-superadmin Administrator users | ||
public function get_test_data__selective_user_modification_caps() { | ||
return [ | ||
'remove_user' => [ 'remove_user' ], | ||
'promote_user' => [ 'promote_user' ], | ||
]; | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__user_modification_caps | ||
*/ | ||
public function test__machine_user_cannot_modify_self( $test_cap ) { | ||
$actual_has_cap = $this->machine_user->has_cap( $test_cap, $this->machine_user->ID ); | ||
|
||
$this->assertFalse( $actual_has_cap ); | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__user_modification_caps | ||
*/ | ||
public function test__non_admin_users_cannot_modify_machine_user( $test_cap ) { | ||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'editor' ] ); | ||
|
||
$actual_has_cap = $test_user->has_cap( $test_cap, $this->machine_user->ID ); | ||
|
||
$this->assertFalse( $actual_has_cap ); | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__user_modification_caps | ||
*/ | ||
public function test__admin_users_cannot_modify_machine_user( $test_cap ) { | ||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'administrator' ] ); | ||
|
||
$actual_has_cap = $test_user->has_cap( $test_cap, $this->machine_user->ID ); | ||
|
||
$this->assertFalse( $actual_has_cap ); | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__user_modification_caps | ||
*/ | ||
public function test__superadmin_users_cannot_modify_machine_user( $test_cap ) { | ||
if ( ! is_multisite() ) { | ||
$this->markTestSkipped( 'No superadmins on single site installs.' ); | ||
} | ||
|
||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'administrator' ] ); | ||
grant_super_admin( $test_user->ID ); | ||
|
||
$actual_has_cap = $test_user->has_cap( $test_cap, $this->machine_user->ID ); | ||
|
||
$this->assertFalse( $actual_has_cap ); | ||
} | ||
|
||
public function test__non_admin_users_can_still_modify_self() { | ||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'editor' ] ); | ||
|
||
$actual_has_cap = $test_user->has_cap( 'edit_user', $test_user->ID ); | ||
|
||
$this->assertTrue( $actual_has_cap ); | ||
} | ||
|
||
public function test__admin_users_can_still_modify_self() { | ||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'administrator' ] ); | ||
|
||
$actual_has_cap = $test_user->has_cap( 'edit_user', $test_user->ID ); | ||
|
||
$this->assertTrue( $actual_has_cap ); | ||
} | ||
|
||
public function test__superadmin_users_can_still_modify_self() { | ||
if ( ! is_multisite() ) { | ||
$this->markTestSkipped( 'No superadmins on single site installs.' ); | ||
} | ||
|
||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'administrator' ] ); | ||
grant_super_admin( $test_user->ID ); | ||
|
||
$actual_has_cap = $test_user->has_cap( 'edit_user', $test_user->ID ); | ||
|
||
$this->assertTrue( $actual_has_cap ); | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__user_modification_caps | ||
*/ | ||
public function test__non_admin_users_cannot_modify_others( $test_cap ) { | ||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'editor' ] ); | ||
$user_to_edit = $this->factory->user->create_and_get( [ 'role' => 'editor' ] ); | ||
|
||
$actual_has_cap = $test_user->has_cap( $test_cap, $user_to_edit->ID ); | ||
|
||
$this->assertFalse( $actual_has_cap ); | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__user_modification_caps | ||
*/ | ||
public function test__admin_users_can_still_modify_others_on_single_site( $test_cap ) { | ||
if ( is_multisite() ) { | ||
$this->markTestSkipped( 'Single site test for administrator user; multisite tested separately.' ); | ||
} | ||
|
||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'administrator' ] ); | ||
$user_to_edit = $this->factory->user->create_and_get( [ 'role' => 'editor' ] ); | ||
|
||
$actual_has_cap = $test_user->has_cap( $test_cap, $user_to_edit->ID ); | ||
|
||
$this->assertTrue( $actual_has_cap ); | ||
} | ||
|
||
/** | ||
* Administrators without super admin have a more restricted set of caps on multisite (no edit or delete). | ||
* | ||
* @dataProvider get_test_data__selective_user_modification_caps | ||
*/ | ||
public function test__admin_users_can_still_selectively_modify_others_on_multsite( $test_cap ) { | ||
if ( ! is_multisite() ) { | ||
$this->markTestSkipped( 'Multisite test for administrator user; single site tested separately.' ); | ||
} | ||
|
||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'administrator' ] ); | ||
$user_to_edit = $this->factory->user->create_and_get( [ 'role' => 'editor' ] ); | ||
|
||
$actual_has_cap = $test_user->has_cap( $test_cap, $user_to_edit->ID ); | ||
|
||
$this->assertTrue( $actual_has_cap ); | ||
} | ||
|
||
/** | ||
* @dataProvider get_test_data__user_modification_caps | ||
*/ | ||
public function test__superadmin_users_can_still_modify_others( $test_cap ) { | ||
if ( ! is_multisite() ) { | ||
$this->markTestSkipped( 'No superadmins on single site installs.' ); | ||
} | ||
|
||
$test_user = $this->factory->user->create_and_get( [ 'role' => 'administrator' ] ); | ||
grant_super_admin( $test_user->ID ); | ||
$user_to_edit = $this->factory->user->create_and_get( [ 'role' => 'editor' ] ); | ||
|
||
$actual_has_cap = $test_user->has_cap( $test_cap, $user_to_edit->ID ); | ||
|
||
$this->assertTrue( $actual_has_cap ); | ||
} | ||
} |