From 1f9c6904bce64de5eed31208f3096478c6ae16c8 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 2 Nov 2016 06:52:30 +0000 Subject: [PATCH] REST API: Add update and delete endpoints to /users/me Now that /users/me is a standalone resource, it should have all the standard endpoints for a resource. Props pento. Fixes #38521 (hopefully). git-svn-id: https://develop.svn.wordpress.org/trunk@39092 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-users-controller.php | 88 +++++++++++++++++- .../tests/rest-api/rest-users-controller.php | 89 +++++++++++++++++++ 2 files changed, 173 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php index f23a6cd49b..9a8cc21245 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php @@ -95,10 +95,30 @@ class WP_REST_Users_Controller extends WP_REST_Controller { ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/me', array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'get_current_item' ), - 'args' => array( - 'context' => array(), + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_current_item' ), + 'args' => array( + 'context' => array(), + ), + ), + array( + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => array( $this, 'update_current_item' ), + 'permission_callback' => array( $this, 'update_current_item_permissions_check' ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), + ), + array( + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_current_item' ), + 'permission_callback' => array( $this, 'delete_current_item_permissions_check' ), + 'args' => array( + 'force' => array( + 'default' => false, + 'description' => __( 'Required to be true, as resource does not support trashing.' ), + ), + 'reassign' => array(), + ), ), 'schema' => array( $this, 'get_public_item_schema' ), )); @@ -567,6 +587,36 @@ class WP_REST_Users_Controller extends WP_REST_Controller { return $response; } + /** + * Checks if a given request has access to update the current user. + * + * @since 4.7.0 + * @access public + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise. + */ + public function update_current_item_permissions_check( $request ) { + $request['id'] = get_current_user_id(); + + return $this->update_item_permissions_check( $request ); + } + + /** + * Updates the current user. + * + * @since 4.7.0 + * @access public + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + function update_current_item( $request ) { + $request['id'] = get_current_user_id(); + + return $this->update_item( $request ); + } + /** * Checks if a given request has access delete a user. * @@ -645,6 +695,36 @@ class WP_REST_Users_Controller extends WP_REST_Controller { return $response; } + /** + * Checks if a given request has access to delete the current user. + * + * @since 4.7.0 + * @access public + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise. + */ + public function delete_current_item_permissions_check( $request ) { + $request['id'] = get_current_user_id(); + + return $this->delete_item_permissions_check( $request ); + } + + /** + * Deletes the current user. + * + * @since 4.7.0 + * @access public + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + function delete_current_item( $request ) { + $request['id'] = get_current_user_id(); + + return $this->delete_item( $request ); + } + /** * Prepares a single user output for response. * diff --git a/tests/phpunit/tests/rest-api/rest-users-controller.php b/tests/phpunit/tests/rest-api/rest-users-controller.php index eccc8695a8..574e5711f8 100644 --- a/tests/phpunit/tests/rest-api/rest-users-controller.php +++ b/tests/phpunit/tests/rest-api/rest-users-controller.php @@ -956,6 +956,15 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase { $user = get_userdata( self::$editor ); $this->assertArrayHasKey( 'editor', $user->caps ); $this->assertArrayNotHasKey( 'administrator', $user->caps ); + + $request = new WP_REST_Request( 'PUT', '/wp/v2/users/me' ); + $request->set_param( 'roles', array( 'administrator' ) ); + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_cannot_edit_roles', $response, 403 ); + $user = get_userdata( self::$editor ); + $this->assertArrayHasKey( 'editor', $user->caps ); + $this->assertArrayNotHasKey( 'administrator', $user->caps ); } public function test_update_user_role_invalid_privilege_deescalation() { @@ -976,6 +985,16 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase { $user = get_userdata( $user_id ); $this->assertArrayHasKey( 'administrator', $user->caps ); $this->assertArrayNotHasKey( 'editor', $user->caps ); + + $request = new WP_REST_Request( 'PUT', '/wp/v2/users/me' ); + $request->set_param( 'roles', array( 'editor' ) ); + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_user_invalid_role', $response, 403 ); + + $user = get_userdata( $user_id ); + $this->assertArrayHasKey( 'administrator', $user->caps ); + $this->assertArrayNotHasKey( 'editor', $user->caps ); } public function test_update_user_role_privilege_deescalation_multisite() { @@ -996,6 +1015,20 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase { $new_data = $response->get_data(); $this->assertEquals( 'editor', $new_data['roles'][0] ); $this->assertNotEquals( 'administrator', $new_data['roles'][0] ); + + $user_id = $this->factory->user->create( array( 'role' => 'administrator' ) ); + + wp_set_current_user( $user_id ); + $user = wp_get_current_user(); + update_site_option( 'site_admins', array( $user->user_login ) ); + + $request = new WP_REST_Request( 'PUT', '/wp/v2/users/me' ); + $request->set_param( 'roles', array( 'editor' ) ); + $response = $this->server->dispatch( $request ); + + $new_data = $response->get_data(); + $this->assertEquals( 'editor', $new_data['roles'][0] ); + $this->assertNotEquals( 'administrator', $new_data['roles'][0] ); } @@ -1012,6 +1045,16 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase { $user = get_userdata( self::$editor ); $this->assertArrayHasKey( 'editor', $user->caps ); $this->assertArrayNotHasKey( 'BeSharp', $user->caps ); + + $request = new WP_REST_Request( 'PUT', '/wp/v2/users/me' ); + $request->set_param( 'roles', array( 'BeSharp' ) ); + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_user_invalid_role', $response, 400 ); + + $user = get_userdata( self::$editor ); + $this->assertArrayHasKey( 'editor', $user->caps ); + $this->assertArrayNotHasKey( 'BeSharp', $user->caps ); } public function test_update_user_without_permission() { @@ -1029,6 +1072,13 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase { $response = $this->server->dispatch( $request ); $this->assertErrorResponse( 'rest_cannot_edit', $response, 403 ); + + $request = new WP_REST_Request( 'PUT', '/wp/v2/users/me' ); + $request->add_header( 'content-type', 'application/x-www-form-urlencoded' ); + $request->set_body_params( $params ); + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_user_invalid_argument', $response, 400 ); } public function test_update_user_invalid_id() { @@ -1066,6 +1116,22 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase { $this->assertEquals( 'Deleted User', $data['name'] ); } + public function test_delete_current_item() { + $user_id = $this->factory->user->create( array( 'role' => 'administrator', 'display_name' => 'Deleted User' ) ); + + wp_set_current_user( $user_id ); + $user = wp_get_current_user(); + update_site_option( 'site_admins', array( $user->user_login ) ); + + $request = new WP_REST_Request( 'DELETE', '/wp/v2/users/me' ); + $request['force'] = true; + $response = $this->server->dispatch( $request ); + + $this->assertEquals( 200, $response->get_status() ); + $data = $response->get_data(); + $this->assertEquals( 'Deleted User', $data['name'] ); + } + public function test_delete_item_no_trash() { $user_id = $this->factory->user->create( array( 'display_name' => 'Deleted User' ) ); @@ -1082,6 +1148,23 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase { $this->assertNotEmpty( $user ); } + public function test_delete_current_item_no_trash() { + $user_id = $this->factory->user->create( array( 'role' => 'administrator' ) ); + + wp_set_current_user( $user_id ); + $user = wp_get_current_user(); + update_site_option( 'site_admins', array( $user->user_login ) ); + + $userdata = get_userdata( $user_id ); // cache for later + $request = new WP_REST_Request( 'DELETE', '/wp/v2/users/me' ); + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 ); + + // Ensure the user still exists + $user = get_user_by( 'id', $user_id ); + $this->assertNotEmpty( $user ); + } + public function test_delete_user_without_permission() { $user_id = $this->factory->user->create(); @@ -1093,6 +1176,12 @@ class WP_Test_REST_Users_Controller extends WP_Test_REST_Controller_Testcase { $response = $this->server->dispatch( $request ); $this->assertErrorResponse( 'rest_user_cannot_delete', $response, 403 ); + + $request = new WP_REST_Request( 'DELETE', '/wp/v2/users/me' ); + $request['force'] = true; + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_user_cannot_delete', $response, 403 ); } public function test_delete_user_invalid_id() {