Automattic / vip-block-data-api

WordPress plugin that provides an API to retrieve Gutenberg content as structured JSON.
http://wpvip.com
GNU General Public License v3.0
103 stars 7 forks source link

Improve default post access to be more restrictive #26

Closed alecgeatches closed 1 year ago

alecgeatches commented 1 year ago

Description

Previously, the Block Data API verified posts were readable using the 'publish' status. This provides basic access limitations to posts in the REST API, but isn't as restrictive as WordPress' built-in REST API.

For example, the Block Data API can be used to access a custom post type with the publish status that isn't allowed by the REST API:

  1. First a custom post type is registered with 'public' => false:

    register_post_type( 'vip-test-post-type', [
        'public' => false,
    ]);
  2. A post is created with that type:

    $ wp post create --post_type="vip-test-post-type" --post_title="Custom post_type" \
           --post_content='<!-- wp:paragraph --><p>Custom post_type content</p><!-- /wp:paragraph -->' \
           --post_status="publish"
    Success: Created post 12.
  3. If the post is queried via the built-in REST API, the post is not accessible:

    GET /wp-json/wp/v2/posts/12
    
    {"code":"rest_post_invalid_id","message":"Invalid post ID.","data":{"status":404}}
  4. However, the same post was accessible through the block data API due to the publish status:

    GET /wp-json/vip-block-data-api/v1/posts/12/blocks
    
    {"blocks":[{"name":"core\/paragraph","attributes":{"content":"Custom post_type content","dropCap":false}}]}

This has been fixed to use logic based on WordPress' WP_REST_Posts_Controller->check_read_permission() function.

The WordPress REST function is not used directly, as instantiating the REST controller and calling check_read_permission() for a single post is not the intended flow of the class, and can sometimes generate warnings from uninitialized variables. Rather than depending on an internal flow of a specialized class, we moved the permission checking defaults into is_post_readable().

Now when the same non-public post is queried by the block API, it is rejected:

GET /wp-json/vip-block-data-api/v1/posts/12/blocks

{"code":"rest_invalid_param","message":"Invalid parameter(s): id","data":{"status":400,"params":{"id":"Invalid parameter."},"details":[]}}

Steps to Test

Three tests were added to verify this functionality in https://github.com/Automattic/vip-block-data-api/commit/5bf567b3fdfc1c9bbd921d0491c4d33fc2600807:

  1. test_rest_api_returns_blocks_for_custom_post_type(): Ensure custom post types with 'public' => true and 'show_in_rest' => true are returned by the Block Data API successfully.
  2. test_rest_api_returns_error_for_non_public_post_type(): Ensure custom post types with 'public' => false result in an invalid ID error.
  3. test_rest_api_returns_error_for_non_rest_post_type(): Ensure custom post types with 'public' => true but 'show_in_rest' => false also result in an invalid ID error.

These match the behavior of the default REST API.