Skip to content
Permalink
Browse files

REST API: Prevent deletion of post revisions.

Allowing the client to delete revisions breaks the "audit trail" functionality. This is not allowed in WordPress and shouldn't be allowed through the API.
While not recommended, a plugin may opt-in to the previous behavior by setting a custom 'delete_post' capability for the revisions post type.

Props dlh, danielbachhuber, TimothyBlynJacobs, azaozz, kadamwhite.
Fixes #43709.



git-svn-id: https://develop.svn.wordpress.org/trunk@45812 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information...
kadamwhite committed Aug 15, 2019
1 parent eb468c4 commit 8f100777e8927c62f642d7d69a2e1d5bd577b468
@@ -72,11 +72,8 @@ function map_meta_cap( $cap, $user_id, ...$args ) {
}
if ( 'revision' == $post->post_type ) {
$post = get_post( $post->post_parent );
if ( ! $post ) {
$caps[] = 'do_not_allow';
break;
}
$caps[] = 'do_not_allow';
break;
}
if ( ( get_option( 'page_for_posts' ) == $post->ID ) || ( get_option( 'page_on_front' ) == $post->ID ) ) {
@@ -349,6 +349,11 @@ public function get_item( $request ) {
return $parent;
}
$parent_post_type = get_post_type_object( $parent->post_type );
if ( ! current_user_can( $parent_post_type->cap->delete_post, $parent->ID ) ) {
return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete revisions of this post.' ), array( 'status' => rest_authorization_required_code() ) );
}
$revision = $this->get_revision( $request['id'] );
if ( is_wp_error( $revision ) ) {
return $revision;
@@ -383,7 +388,12 @@ public function delete_item_permissions_check( $request ) {
}
$post_type = get_post_type_object( 'revision' );
return current_user_can( $post_type->cap->delete_post, $revision->ID );
if ( ! current_user_can( $post_type->cap->delete_post, $revision->ID ) ) {
return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this revision.' ), array( 'status' => rest_authorization_required_code() ) );
}
return true;
}
/**
@@ -76,6 +76,27 @@ public function setUp() {
$this->revision_id3 = $this->revision_3->ID;
}
public function tearDown() {
parent::tearDown();
remove_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ) );
}
public function _filter_map_meta_cap_remove_no_allow_revisions( $caps, $cap, $user_id, $args ) {
if ( 'delete_post' !== $cap || empty( $args ) ) {
return $caps;
}
$post = get_post( $args[0] );
if ( ! $post || 'revision' !== $post->post_type ) {
return $caps;
}
$key = array_search( 'do_not_allow', $caps, true );
if ( false !== $key ) {
unset( $caps[ $key ] );
}
return $caps;
}
public function test_register_routes() {
$routes = rest_get_server()->get_routes();
$this->assertArrayHasKey( '/wp/v2/posts/(?P<parent>[\d]+)/revisions', $routes );
@@ -216,13 +237,32 @@ public function test_delete_item() {
$request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
$request->set_param( 'force', true );
$response = rest_get_server()->dispatch( $request );
$this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
$this->assertNotNull( get_post( $this->revision_id1 ) );
}
public function test_delete_item_remove_do_not_allow() {
wp_set_current_user( self::$editor_id );
add_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ), 10, 4 );
$request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
$request->set_param( 'force', true );
$response = rest_get_server()->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$this->assertNull( get_post( $this->revision_id1 ) );
}
public function test_delete_item_no_trash() {
public function test_delete_item_cannot_delete_parent() {
wp_set_current_user( self::$editor_id );
$request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
$request->set_param( 'force', true );
$response = rest_get_server()->dispatch( $request );
$this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
$this->assertNotNull( get_post( $this->revision_id1 ) );
}
public function test_delete_item_no_trash() {
wp_set_current_user( self::$editor_id );
add_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ), 10, 4 );
$request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
$response = rest_get_server()->dispatch( $request );
$this->assertErrorResponse( 'rest_trash_not_supported', $response, 501 );

0 comments on commit 8f10077

Please sign in to comment.
You can’t perform that action at this time.