REST API: Add support for comments of password-protected posts.

Core requires the post password to view and create comments on password protected posts, so we must support a “password” param on the comments endpoint when fetch comments for a specific post and creating a comment on a password protected post.

Props flixos90, jnylen0.
Fixes #38692.

git-svn-id: https://develop.svn.wordpress.org/trunk@39349 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Joe Hoyle 2016-11-23 16:14:08 +00:00
parent 24abde4e54
commit 5d7118d63e
3 changed files with 172 additions and 19 deletions

View File

@ -69,6 +69,10 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
'permission_callback' => array( $this, 'get_item_permissions_check' ), 'permission_callback' => array( $this, 'get_item_permissions_check' ),
'args' => array( 'args' => array(
'context' => $this->get_context_param( array( 'default' => 'view' ) ), 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
'password' => array(
'description' => __( 'The password for the post if it is password protected.' ),
'type' => 'string',
),
), ),
), ),
array( array(
@ -87,6 +91,10 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
'default' => false, 'default' => false,
'description' => __( 'Whether to bypass trash and force deletion.' ), 'description' => __( 'Whether to bypass trash and force deletion.' ),
), ),
'password' => array(
'description' => __( 'The password for the post if it is password protected.' ),
'type' => 'string',
),
), ),
), ),
'schema' => array( $this, 'get_public_item_schema' ), 'schema' => array( $this, 'get_public_item_schema' ),
@ -108,7 +116,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
foreach ( (array) $request['post'] as $post_id ) { foreach ( (array) $request['post'] as $post_id ) {
$post = get_post( $post_id ); $post = get_post( $post_id );
if ( ! empty( $post_id ) && $post && ! $this->check_read_post_permission( $post ) ) { if ( ! empty( $post_id ) && $post && ! $this->check_read_post_permission( $post, $request ) ) {
return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you are not allowed to read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you are not allowed to read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) );
} elseif ( 0 === $post_id && ! current_user_can( 'moderate_comments' ) ) { } elseif ( 0 === $post_id && ! current_user_can( 'moderate_comments' ) ) {
return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to read comments without a post.' ), array( 'status' => rest_authorization_required_code() ) ); return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to read comments without a post.' ), array( 'status' => rest_authorization_required_code() ) );
@ -242,7 +250,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
$comments = array(); $comments = array();
foreach ( $query_result as $comment ) { foreach ( $query_result as $comment ) {
if ( ! $this->check_read_permission( $comment ) ) { if ( ! $this->check_read_permission( $comment, $request ) ) {
continue; continue;
} }
@ -309,18 +317,18 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
return true; return true;
} }
if ( ! $this->check_read_permission( $comment ) ) { if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) {
return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to read this comment.' ), array( 'status' => rest_authorization_required_code() ) ); return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit comments.' ), array( 'status' => rest_authorization_required_code() ) );
} }
$post = get_post( $comment->comment_post_ID ); $post = get_post( $comment->comment_post_ID );
if ( $post && ! $this->check_read_post_permission( $post ) ) { if ( ! $this->check_read_permission( $comment, $request ) ) {
return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you are not allowed to read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to read this comment.' ), array( 'status' => rest_authorization_required_code() ) );
} }
if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) { if ( $post && ! $this->check_read_post_permission( $post, $request ) ) {
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit comments.' ), array( 'status' => rest_authorization_required_code() ) ); return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you are not allowed to read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) );
} }
return true; return true;
@ -433,7 +441,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
return new WP_Error( 'rest_comment_trash_post', __( 'Sorry, you are not allowed to create a comment on this post.' ), array( 'status' => 403 ) ); return new WP_Error( 'rest_comment_trash_post', __( 'Sorry, you are not allowed to create a comment on this post.' ), array( 'status' => 403 ) );
} }
if ( ! $this->check_read_post_permission( $post ) ) { if ( ! $this->check_read_post_permission( $post, $request ) ) {
return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you are not allowed to read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) ); return new WP_Error( 'rest_cannot_read_post', __( 'Sorry, you are not allowed to read the post for this comment.' ), array( 'status' => rest_authorization_required_code() ) );
} }
@ -1412,6 +1420,11 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
'validate_callback' => 'rest_validate_request_arg', 'validate_callback' => 'rest_validate_request_arg',
); );
$query_params['password'] = array(
'description' => __( 'The password for the post if it is password protected.' ),
'type' => 'string',
);
/** /**
* Filter collection parameters for the comments controller. * Filter collection parameters for the comments controller.
* *
@ -1481,18 +1494,36 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
* @since 4.7.0 * @since 4.7.0
* @access protected * @access protected
* *
* @param WP_Post $post Post Object. * @param WP_Post $post Post object.
* @param WP_REST_Request $request Request data to check.
* @return bool Whether post can be read. * @return bool Whether post can be read.
*/ */
protected function check_read_post_permission( $post ) { protected function check_read_post_permission( $post, $request ) {
$posts_controller = new WP_REST_Posts_Controller( $post->post_type ); $posts_controller = new WP_REST_Posts_Controller( $post->post_type );
$post_type = get_post_type_object( $post->post_type ); $post_type = get_post_type_object( $post->post_type );
if ( post_password_required( $post ) ) { $has_password_filter = false;
return current_user_can( $post_type->cap->edit_post, $post->ID );
// Only check password if a specific post was queried for or a single comment
$requested_post = ! empty( $request['post'] ) && 1 === count( $request['post'] );
$requested_comment = ! empty( $request['id'] );
if ( ( $requested_post || $requested_comment ) && $posts_controller->can_access_password_content( $post, $request ) ) {
add_filter( 'post_password_required', '__return_false' );
$has_password_filter = true;
} }
return $posts_controller->check_read_permission( $post ); if ( post_password_required( $post ) ) {
$result = current_user_can( $post_type->cap->edit_post, $post->ID );
} else {
$result = $posts_controller->check_read_permission( $post );
}
if ( $has_password_filter ) {
remove_filter( 'post_password_required', '__return_false' );
}
return $result;
} }
/** /**
@ -1501,14 +1532,15 @@ class WP_REST_Comments_Controller extends WP_REST_Controller {
* @since 4.7.0 * @since 4.7.0
* @access protected * @access protected
* *
* @param WP_Comment $comment Comment object. * @param WP_Comment $comment Comment object.
* @param WP_REST_Request $request Request data to check.
* @return bool Whether the comment can be read. * @return bool Whether the comment can be read.
*/ */
protected function check_read_permission( $comment ) { protected function check_read_permission( $comment, $request ) {
if ( ! empty( $comment->comment_post_ID ) ) { if ( ! empty( $comment->comment_post_ID ) ) {
$post = get_post( $comment->comment_post_ID ); $post = get_post( $comment->comment_post_ID );
if ( $post ) { if ( $post ) {
if ( $this->check_read_post_permission( $post ) && 1 === (int) $comment->comment_approved ) { if ( $this->check_read_post_permission( $post, $request ) && 1 === (int) $comment->comment_approved ) {
return true; return true;
} }
} }

View File

@ -383,13 +383,13 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
* check in core with a filter. * check in core with a filter.
* *
* @since 4.7.0 * @since 4.7.0
* @access protected * @access public
* *
* @param WP_Post $post Post to check against. * @param WP_Post $post Post to check against.
* @param WP_REST_Request $request Request data to check. * @param WP_REST_Request $request Request data to check.
* @return bool True if the user can access password-protected content, otherwise false. * @return bool True if the user can access password-protected content, otherwise false.
*/ */
protected function can_access_password_content( $post, $request ) { public function can_access_password_content( $post, $request ) {
if ( empty( $post->post_password ) ) { if ( empty( $post->post_password ) ) {
// No filter required. // No filter required.
return false; return false;

View File

@ -146,6 +146,7 @@ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase
'page', 'page',
'parent', 'parent',
'parent_exclude', 'parent_exclude',
'password',
'per_page', 'per_page',
'post', 'post',
'search', 'search',
@ -167,6 +168,69 @@ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase
$this->assertCount( 7, $comments ); $this->assertCount( 7, $comments );
} }
/**
* @ticket 38692
*/
public function test_get_items_with_password() {
wp_set_current_user( 0 );
$args = array(
'comment_approved' => 1,
'comment_post_ID' => self::$password_id,
);
$password_comment = $this->factory->comment->create( $args );
$request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
$request->set_param( 'password', 'toomanysecrets' );
$request->set_param( 'post', self::$password_id );
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$collection_data = $response->get_data();
$this->assertTrue( in_array( $password_comment, wp_list_pluck( $collection_data, 'id' ), true ) );
}
/**
* @ticket 38692
*/
public function test_get_items_with_password_without_post() {
wp_set_current_user( 0 );
$args = array(
'comment_approved' => 1,
'comment_post_ID' => self::$password_id,
);
$password_comment = $this->factory->comment->create( $args );
$request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
$request->set_param( 'password', 'toomanysecrets' );
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$collection_data = $response->get_data();
$this->assertFalse( in_array( $password_comment, wp_list_pluck( $collection_data, 'id' ), true ) );
}
/**
* @ticket 38692
*/
public function test_get_items_with_password_with_multiple_post() {
wp_set_current_user( 0 );
$args = array(
'comment_approved' => 1,
'comment_post_ID' => self::$password_id,
);
$password_comment = $this->factory->comment->create( $args );
$request = new WP_REST_Request( 'GET', '/wp/v2/comments' );
$request->set_param( 'password', 'toomanysecrets' );
$request->set_param( 'post', array( self::$password_id, self::$post_id ) );
$response = $this->server->dispatch( $request );
$this->assertErrorResponse( 'rest_cannot_read_post', $response, 401 );
}
public function test_get_password_items_without_edit_post_permission() { public function test_get_password_items_without_edit_post_permission() {
wp_set_current_user( 0 ); wp_set_current_user( 0 );
@ -853,6 +917,25 @@ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase
$this->assertErrorResponse( 'rest_cannot_read', $response, 403 ); $this->assertErrorResponse( 'rest_cannot_read', $response, 403 );
} }
/**
* @ticket 38692
*/
public function test_get_comment_with_password_with_valid_password() {
wp_set_current_user( self::$subscriber_id );
$args = array(
'comment_approved' => 1,
'comment_post_ID' => self::$password_id,
);
$password_comment = $this->factory->comment->create( $args );
$request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/comments/%s', $password_comment ) );
$request->set_param( 'password', 'toomanysecrets' );
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
}
public function test_create_item() { public function test_create_item() {
wp_set_current_user( self::$admin_id ); wp_set_current_user( self::$admin_id );
@ -1727,6 +1810,44 @@ class WP_Test_REST_Comments_Controller extends WP_Test_REST_Controller_Testcase
$this->assertErrorResponse( 'comment_content_column_length', $response, 400 ); $this->assertErrorResponse( 'comment_content_column_length', $response, 400 );
} }
public function test_create_comment_without_password() {
wp_set_current_user( self::$subscriber_id );
$params = array(
'post' => self::$password_id,
'author_name' => 'Bleeding Gums Murphy',
'author_email' => 'murphy@gingivitis.com',
'author_url' => 'http://jazz.gingivitis.com',
'content' => 'This isn\'t a saxophone. It\'s an umbrella.',
);
$request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
$request->add_header( 'content-type', 'application/json' );
$request->set_body( wp_json_encode( $params ) );
$response = $this->server->dispatch( $request );
$this->assertErrorResponse( 'rest_cannot_read_post', $response, 403 );
}
public function test_create_comment_with_password() {
add_filter( 'rest_allow_anonymous_comments', '__return_true' );
$params = array(
'post' => self::$password_id,
'author_name' => 'Bleeding Gums Murphy',
'author_email' => 'murphy@gingivitis.com',
'author_url' => 'http://jazz.gingivitis.com',
'content' => 'This isn\'t a saxophone. It\'s an umbrella.',
'password' => 'toomanysecrets',
);
$request = new WP_REST_Request( 'POST', '/wp/v2/comments' );
$request->add_header( 'content-type', 'application/json' );
$request->set_body( wp_json_encode( $params ) );
$response = $this->server->dispatch( $request );
$this->assertEquals( 201, $response->get_status() );
}
public function test_update_item() { public function test_update_item() {
$post_id = $this->factory->post->create(); $post_id = $this->factory->post->create();