From c0e80b028ae57e6d32b08c7aa6eceecb86a7287d Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers Date: Tue, 11 Dec 2018 22:29:36 +0000 Subject: [PATCH] REST API: Enable users with read_private_posts to query for them. An authorized request with the read_private_posts capability for a post type should be able to GET /wp/v2/posts for posts of status=private. This query is further sanity-checked by WP_REST_Posts_Controller->check_read_permission(), which is unchanged. Props rachelbaker, soulseekah, twoelevenjay. Moves [43694] from the 5.0 branch to trunk. Fixes #43701. git-svn-id: https://develop.svn.wordpress.org/trunk@43979 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/rest-api.php | 2 +- .../class-wp-rest-posts-controller.php | 2 +- .../tests/rest-api/rest-posts-controller.php | 70 +++++++++++++++++-- 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index 0cce9fa62f..35bab60712 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -990,7 +990,7 @@ function rest_sanitize_boolean( $value ) { } // Everything else will map nicely to boolean. - return (boolean) $value; + return (bool) $value; } /** diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index bff977e40c..875b0518eb 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -2509,7 +2509,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { $post_type_obj = get_post_type_object( $this->post_type ); - if ( current_user_can( $post_type_obj->cap->edit_posts ) ) { + if ( current_user_can( $post_type_obj->cap->edit_posts ) || 'private' === $status && current_user_can( $post_type_obj->cap->read_private_posts ) ) { $result = rest_validate_request_arg( $status, $request, $parameter ); if ( is_wp_error( $result ) ) { return $result; diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index ded82940ee..9a7790e11b 100644 --- a/tests/phpunit/tests/rest-api/rest-posts-controller.php +++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php @@ -16,6 +16,7 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te protected static $editor_id; protected static $author_id; protected static $contributor_id; + protected static $private_reader_id; protected static $supported_formats; @@ -47,6 +48,12 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te ) ); + self::$private_reader_id = $factory->user->create( + array( + 'role' => 'private_reader', + ) + ); + if ( is_multisite() ) { update_site_option( 'site_admins', array( 'superadmin' ) ); } @@ -70,6 +77,7 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te self::delete_user( self::$editor_id ); self::delete_user( self::$author_id ); self::delete_user( self::$contributor_id ); + self::delete_user( self::$private_reader_id ); } public function setUp() { @@ -81,6 +89,11 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te 'show_in_rest' => true, ) ); + + add_role( 'private_reader', 'Private Reader' ); + $role = get_role( 'private_reader' ); + $role->add_cap( 'read_private_posts' ); + add_filter( 'rest_pre_dispatch', array( $this, 'wpSetUpBeforeRequest' ), 10, 3 ); add_filter( 'posts_clauses', array( $this, 'save_posts_clauses' ), 10, 2 ); } @@ -592,6 +605,20 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); } + /** + * @ticket 43701 + */ + public function test_get_items_multiple_statuses_custom_role_one_invalid_query() { + $private_post_id = $this->factory->post->create( array( 'post_status' => 'private' ) ); + + wp_set_current_user( self::$private_reader_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'status', array( 'private', 'future' ) ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + } + public function test_get_items_invalid_status_query() { wp_set_current_user( 0 ); $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); @@ -1194,23 +1221,54 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] ); } - public function test_get_items_private_status_query_var() { - // Private query vars inaccessible to unauthorized users - wp_set_current_user( 0 ); + public function test_get_items_status_draft_permissions() { $draft_id = $this->factory->post->create( array( 'post_status' => 'draft' ) ); - $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + + // Drafts status query var inaccessible to unauthorized users. + wp_set_current_user( 0 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $request->set_param( 'status', 'draft' ); $response = rest_get_server()->dispatch( $request ); $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); - // But they are accessible to authorized users + // Users with 'read_private_posts' cap shouldn't also be able to view drafts. + wp_set_current_user( self::$private_reader_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'status', 'draft' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + + // But drafts are accessible to authorized users. wp_set_current_user( self::$editor_id ); $response = rest_get_server()->dispatch( $request ); $data = $response->get_data(); - $this->assertCount( 1, $data ); + $this->assertEquals( $draft_id, $data[0]['id'] ); } + /** + * @ticket 43701 + */ + public function test_get_items_status_private_permissions() { + $private_post_id = $this->factory->post->create( array( 'post_status' => 'private' ) ); + + wp_set_current_user( 0 ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'status', 'private' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + + wp_set_current_user( self::$private_reader_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); + $request->set_param( 'status', 'private' ); + + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $this->assertEquals( 200, $response->get_status() ); + $this->assertCount( 1, $data ); + $this->assertEquals( $private_post_id, $data[0]['id'] ); + } + public function test_get_items_invalid_per_page() { $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $request->set_query_params( array( 'per_page' => -1 ) );