Skip to content

Commit c0e80b0

Browse files
committed
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
1 parent f173141 commit c0e80b0

3 files changed

Lines changed: 66 additions & 8 deletions

File tree

src/wp-includes/rest-api.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -990,7 +990,7 @@ function rest_sanitize_boolean( $value ) {
990990
}
991991

992992
// Everything else will map nicely to boolean.
993-
return (boolean) $value;
993+
return (bool) $value;
994994
}
995995

996996
/**

src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2509,7 +2509,7 @@ public function sanitize_post_statuses( $statuses, $request, $parameter ) {
25092509

25102510
$post_type_obj = get_post_type_object( $this->post_type );
25112511

2512-
if ( current_user_can( $post_type_obj->cap->edit_posts ) ) {
2512+
if ( current_user_can( $post_type_obj->cap->edit_posts ) || 'private' === $status && current_user_can( $post_type_obj->cap->read_private_posts ) ) {
25132513
$result = rest_validate_request_arg( $status, $request, $parameter );
25142514
if ( is_wp_error( $result ) ) {
25152515
return $result;

tests/phpunit/tests/rest-api/rest-posts-controller.php

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te
1616
protected static $editor_id;
1717
protected static $author_id;
1818
protected static $contributor_id;
19+
protected static $private_reader_id;
1920

2021
protected static $supported_formats;
2122

@@ -47,6 +48,12 @@ public static function wpSetUpBeforeClass( $factory ) {
4748
)
4849
);
4950

51+
self::$private_reader_id = $factory->user->create(
52+
array(
53+
'role' => 'private_reader',
54+
)
55+
);
56+
5057
if ( is_multisite() ) {
5158
update_site_option( 'site_admins', array( 'superadmin' ) );
5259
}
@@ -70,6 +77,7 @@ public static function wpTearDownAfterClass() {
7077
self::delete_user( self::$editor_id );
7178
self::delete_user( self::$author_id );
7279
self::delete_user( self::$contributor_id );
80+
self::delete_user( self::$private_reader_id );
7381
}
7482

7583
public function setUp() {
@@ -81,6 +89,11 @@ public function setUp() {
8189
'show_in_rest' => true,
8290
)
8391
);
92+
93+
add_role( 'private_reader', 'Private Reader' );
94+
$role = get_role( 'private_reader' );
95+
$role->add_cap( 'read_private_posts' );
96+
8497
add_filter( 'rest_pre_dispatch', array( $this, 'wpSetUpBeforeRequest' ), 10, 3 );
8598
add_filter( 'posts_clauses', array( $this, 'save_posts_clauses' ), 10, 2 );
8699
}
@@ -592,6 +605,20 @@ public function test_get_items_multiple_statuses_one_invalid_query() {
592605
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
593606
}
594607

608+
/**
609+
* @ticket 43701
610+
*/
611+
public function test_get_items_multiple_statuses_custom_role_one_invalid_query() {
612+
$private_post_id = $this->factory->post->create( array( 'post_status' => 'private' ) );
613+
614+
wp_set_current_user( self::$private_reader_id );
615+
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
616+
$request->set_param( 'status', array( 'private', 'future' ) );
617+
618+
$response = rest_get_server()->dispatch( $request );
619+
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
620+
}
621+
595622
public function test_get_items_invalid_status_query() {
596623
wp_set_current_user( 0 );
597624
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
@@ -1194,23 +1221,54 @@ public function test_get_items_pagination_headers() {
11941221
$this->assertContains( '<' . $next_link . '>; rel="next"', $headers['Link'] );
11951222
}
11961223

1197-
public function test_get_items_private_status_query_var() {
1198-
// Private query vars inaccessible to unauthorized users
1199-
wp_set_current_user( 0 );
1224+
public function test_get_items_status_draft_permissions() {
12001225
$draft_id = $this->factory->post->create( array( 'post_status' => 'draft' ) );
1201-
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
1226+
1227+
// Drafts status query var inaccessible to unauthorized users.
1228+
wp_set_current_user( 0 );
1229+
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
12021230
$request->set_param( 'status', 'draft' );
12031231
$response = rest_get_server()->dispatch( $request );
12041232
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
12051233

1206-
// But they are accessible to authorized users
1234+
// Users with 'read_private_posts' cap shouldn't also be able to view drafts.
1235+
wp_set_current_user( self::$private_reader_id );
1236+
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
1237+
$request->set_param( 'status', 'draft' );
1238+
$response = rest_get_server()->dispatch( $request );
1239+
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
1240+
1241+
// But drafts are accessible to authorized users.
12071242
wp_set_current_user( self::$editor_id );
12081243
$response = rest_get_server()->dispatch( $request );
12091244
$data = $response->get_data();
1210-
$this->assertCount( 1, $data );
1245+
12111246
$this->assertEquals( $draft_id, $data[0]['id'] );
12121247
}
12131248

1249+
/**
1250+
* @ticket 43701
1251+
*/
1252+
public function test_get_items_status_private_permissions() {
1253+
$private_post_id = $this->factory->post->create( array( 'post_status' => 'private' ) );
1254+
1255+
wp_set_current_user( 0 );
1256+
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
1257+
$request->set_param( 'status', 'private' );
1258+
$response = rest_get_server()->dispatch( $request );
1259+
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
1260+
1261+
wp_set_current_user( self::$private_reader_id );
1262+
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
1263+
$request->set_param( 'status', 'private' );
1264+
1265+
$response = rest_get_server()->dispatch( $request );
1266+
$data = $response->get_data();
1267+
$this->assertEquals( 200, $response->get_status() );
1268+
$this->assertCount( 1, $data );
1269+
$this->assertEquals( $private_post_id, $data[0]['id'] );
1270+
}
1271+
12141272
public function test_get_items_invalid_per_page() {
12151273
$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
12161274
$request->set_query_params( array( 'per_page' => -1 ) );

0 commit comments

Comments
 (0)