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
This commit is contained in:
Jonathan Desrosiers 2018-12-11 22:29:36 +00:00
parent f173141c5b
commit c0e80b028a
3 changed files with 66 additions and 8 deletions

View File

@ -990,7 +990,7 @@ function rest_sanitize_boolean( $value ) {
} }
// Everything else will map nicely to boolean. // Everything else will map nicely to boolean.
return (boolean) $value; return (bool) $value;
} }
/** /**

View File

@ -2509,7 +2509,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
$post_type_obj = get_post_type_object( $this->post_type ); $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 ); $result = rest_validate_request_arg( $status, $request, $parameter );
if ( is_wp_error( $result ) ) { if ( is_wp_error( $result ) ) {
return $result; return $result;

View File

@ -16,6 +16,7 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te
protected static $editor_id; protected static $editor_id;
protected static $author_id; protected static $author_id;
protected static $contributor_id; protected static $contributor_id;
protected static $private_reader_id;
protected static $supported_formats; 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() ) { if ( is_multisite() ) {
update_site_option( 'site_admins', array( 'superadmin' ) ); 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::$editor_id );
self::delete_user( self::$author_id ); self::delete_user( self::$author_id );
self::delete_user( self::$contributor_id ); self::delete_user( self::$contributor_id );
self::delete_user( self::$private_reader_id );
} }
public function setUp() { 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, '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( 'rest_pre_dispatch', array( $this, 'wpSetUpBeforeRequest' ), 10, 3 );
add_filter( 'posts_clauses', array( $this, 'save_posts_clauses' ), 10, 2 ); 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 ); $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() { public function test_get_items_invalid_status_query() {
wp_set_current_user( 0 ); wp_set_current_user( 0 );
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $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'] ); $this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] );
} }
public function test_get_items_private_status_query_var() { public function test_get_items_status_draft_permissions() {
// Private query vars inaccessible to unauthorized users
wp_set_current_user( 0 );
$draft_id = $this->factory->post->create( array( 'post_status' => 'draft' ) ); $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' ); $request->set_param( 'status', 'draft' );
$response = rest_get_server()->dispatch( $request ); $response = rest_get_server()->dispatch( $request );
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); $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 ); wp_set_current_user( self::$editor_id );
$response = rest_get_server()->dispatch( $request ); $response = rest_get_server()->dispatch( $request );
$data = $response->get_data(); $data = $response->get_data();
$this->assertCount( 1, $data );
$this->assertEquals( $draft_id, $data[0]['id'] ); $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() { public function test_get_items_invalid_per_page() {
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
$request->set_query_params( array( 'per_page' => -1 ) ); $request->set_query_params( array( 'per_page' => -1 ) );