REST API: Allow authors to read their own password protected posts.

Allow authenticated users to read the contents of password protected posts if they have the `edit_post` meta capability for the post.

Props xknown, zieladam, peterwilsoncc, swissspidy, timothyblynjacobs.

git-svn-id: https://develop.svn.wordpress.org/trunk@50717 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Jonathan Desrosiers 2021-04-14 23:22:07 +00:00
parent 60fa61de5b
commit 312b67e82a
2 changed files with 80 additions and 7 deletions

View File

@ -31,6 +31,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
*/
protected $meta;
/**
* Passwordless post access permitted.
*
* @since 5.7.1
* @var int[]
*/
protected $password_check_passed = array();
/**
* Constructor.
*
@ -148,6 +156,38 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
return true;
}
/**
* Override the result of the post password check for REST requested posts.
*
* Allow users to read the content of password protected posts if they have
* previously passed a permission check or if they have the `edit_post` capability
* for the post being checked.
*
* @since 5.7.1
*
* @param bool $required Whether the post requires a password check.
* @param WP_Post $post The post been password checked.
* @return bool Result of password check taking in to account REST API considerations.
*/
public function check_password_required( $required, $post ) {
if ( ! $required ) {
return $required;
}
$post = get_post( $post );
if ( ! $post ) {
return $required;
}
if ( ! empty( $this->password_check_passed[ $post->ID ] ) ) {
// Password previously checked and approved.
return false;
}
return ! current_user_can( 'edit_post', $post->ID );
}
/**
* Retrieves a collection of posts.
*
@ -314,7 +354,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
// Allow access to all password protected posts if the context is edit.
if ( 'edit' === $request['context'] ) {
add_filter( 'post_password_required', '__return_false' );
add_filter( 'post_password_required', array( $this, 'check_password_required' ), 10, 2 );
}
$posts = array();
@ -330,7 +370,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
// Reset filter.
if ( 'edit' === $request['context'] ) {
remove_filter( 'post_password_required', '__return_false' );
remove_filter( 'post_password_required', array( $this, 'check_password_required' ) );
}
$page = (int) $query_args['paged'];
@ -445,7 +485,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
// Allow access to all password protected posts if the context is edit.
if ( 'edit' === $request['context'] ) {
add_filter( 'post_password_required', '__return_false' );
add_filter( 'post_password_required', array( $this, 'check_password_required' ), 10, 2 );
}
if ( $post ) {
@ -473,8 +513,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
return false;
}
// Edit context always gets access to password-protected posts.
if ( 'edit' === $request['context'] ) {
/*
* Users always gets access to password protected content in the edit
* context if they have the `edit_post` meta capability.
*/
if (
'edit' === $request['context'] &&
current_user_can( 'edit_post', $post->ID )
) {
return true;
}
@ -1728,8 +1774,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
$has_password_filter = false;
if ( $this->can_access_password_content( $post, $request ) ) {
$this->password_check_passed[ $post->ID ] = true;
// Allow access to the post, permissions already checked before.
add_filter( 'post_password_required', '__return_false' );
add_filter( 'post_password_required', array( $this, 'check_password_required' ), 10, 2 );
$has_password_filter = true;
}
@ -1767,7 +1814,7 @@ class WP_REST_Posts_Controller extends WP_REST_Controller {
if ( $has_password_filter ) {
// Reset filter.
remove_filter( 'post_password_required', '__return_false' );
remove_filter( 'post_password_required', array( $this, 'check_password_required' ) );
}
if ( rest_is_field_included( 'author', $fields ) ) {

View File

@ -1820,6 +1820,32 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te
$this->assertErrorResponse( 'rest_forbidden', $response, 401 );
}
public function test_get_post_draft_edit_context() {
$post_content = 'Hello World!';
$this->factory->post->create(
array(
'post_title' => 'Hola',
'post_password' => 'password',
'post_content' => $post_content,
'post_excerpt' => $post_content,
'post_author' => self::$editor_id,
)
);
$draft_id = $this->factory->post->create(
array(
'post_status' => 'draft',
'post_author' => self::$contributor_id,
'post_content' => '<!-- wp:latest-posts {"displayPostContent":true} /--> <!-- wp:latest-posts {"displayPostContent":true,"displayPostContentRadio":"full_post"} /-->',
)
);
wp_set_current_user( self::$contributor_id );
$request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/posts/%d', $draft_id ) );
$request->set_param( 'context', 'edit' );
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$this->assertNotContains( $post_content, $data['content']['rendered'] );
}
public function test_get_post_invalid_id() {
$request = new WP_REST_Request( 'GET', '/wp/v2/posts/' . REST_TESTS_IMPOSSIBLY_HIGH_NUMBER );
$response = rest_get_server()->dispatch( $request );