Make WordPress Core

Changeset 45812


Ignore:
Timestamp:
08/15/2019 10:00:49 PM (5 years ago)
Author:
kadamwhite
Message:

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.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/capabilities.php

    r45734 r45812  
    7373
    7474            if ( 'revision' == $post->post_type ) {
    75                 $post = get_post( $post->post_parent );
    76                 if ( ! $post ) {
    77                     $caps[] = 'do_not_allow';
    78                     break;
    79                 }
     75                $caps[] = 'do_not_allow';
     76                break;
    8077            }
    8178
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php

    r45811 r45812  
    350350        }
    351351
     352        $parent_post_type = get_post_type_object( $parent->post_type );
     353        if ( ! current_user_can( $parent_post_type->cap->delete_post, $parent->ID ) ) {
     354            return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete revisions of this post.' ), array( 'status' => rest_authorization_required_code() ) );
     355        }
     356
    352357        $revision = $this->get_revision( $request['id'] );
    353358        if ( is_wp_error( $revision ) ) {
     
    384389
    385390        $post_type = get_post_type_object( 'revision' );
    386         return current_user_can( $post_type->cap->delete_post, $revision->ID );
     391
     392        if ( ! current_user_can( $post_type->cap->delete_post, $revision->ID ) ) {
     393            return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this revision.' ), array( 'status' => rest_authorization_required_code() ) );
     394        }
     395
     396        return true;
    387397    }
    388398
  • trunk/tests/phpunit/tests/rest-api/rest-revisions-controller.php

    r43647 r45812  
    7575        $this->revision_3      = array_pop( $revisions );
    7676        $this->revision_id3    = $this->revision_3->ID;
     77    }
     78
     79    public function tearDown() {
     80        parent::tearDown();
     81
     82        remove_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ) );
     83    }
     84
     85    public function _filter_map_meta_cap_remove_no_allow_revisions( $caps, $cap, $user_id, $args ) {
     86        if ( 'delete_post' !== $cap || empty( $args ) ) {
     87            return $caps;
     88        }
     89        $post = get_post( $args[0] );
     90        if ( ! $post || 'revision' !== $post->post_type ) {
     91            return $caps;
     92        }
     93        $key = array_search( 'do_not_allow', $caps, true );
     94        if ( false !== $key ) {
     95            unset( $caps[ $key ] );
     96        }
     97        return $caps;
    7798    }
    7899
     
    217238        $request->set_param( 'force', true );
    218239        $response = rest_get_server()->dispatch( $request );
     240        $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
     241        $this->assertNotNull( get_post( $this->revision_id1 ) );
     242    }
     243
     244    public function test_delete_item_remove_do_not_allow() {
     245        wp_set_current_user( self::$editor_id );
     246        add_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ), 10, 4 );
     247        $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
     248        $request->set_param( 'force', true );
     249        $response = rest_get_server()->dispatch( $request );
    219250        $this->assertEquals( 200, $response->get_status() );
    220251        $this->assertNull( get_post( $this->revision_id1 ) );
    221252    }
    222253
     254    public function test_delete_item_cannot_delete_parent() {
     255        wp_set_current_user( self::$editor_id );
     256        $request = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
     257        $request->set_param( 'force', true );
     258        $response = rest_get_server()->dispatch( $request );
     259        $this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
     260        $this->assertNotNull( get_post( $this->revision_id1 ) );
     261    }
     262
    223263    public function test_delete_item_no_trash() {
    224264        wp_set_current_user( self::$editor_id );
    225 
     265        add_filter( 'map_meta_cap', array( $this, '_filter_map_meta_cap_remove_no_allow_revisions' ), 10, 4 );
    226266        $request  = new WP_REST_Request( 'DELETE', '/wp/v2/posts/' . self::$post_id . '/revisions/' . $this->revision_id1 );
    227267        $response = rest_get_server()->dispatch( $request );
Note: See TracChangeset for help on using the changeset viewer.