WordPress.org

Make WordPress Core

Changeset 49380 for trunk


Ignore:
Timestamp:
10/29/2020 05:42:13 PM (8 months ago)
Author:
desrosj
Message:

XML-RPC: Improve error messages for unprivileged users.

Add specific permission checks to avoid ambiguous failure messages.

Props zieladam, peterwilsoncc, xknown, whyisjake.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-xmlrpc-server.php

    r49303 r49380  
    38773877        }
    38783878
     3879        if (
     3880            'publish' === get_post_status( $post_id ) &&
     3881            ! current_user_can( 'edit_post', $post_id ) &&
     3882            post_password_required( $post_id )
     3883        ) {
     3884            return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) );
     3885        }
     3886
     3887        if (
     3888            'private' === get_post_status( $post_id ) &&
     3889            ! current_user_can( 'read_post', $post_id )
     3890        ) {
     3891            return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) );
     3892        }
     3893
    38793894        $comment = array(
    38803895            'comment_post_ID' => $post_id,
  • trunk/tests/phpunit/tests/xmlrpc/wp/newComment.php

    r49303 r49380  
    77
    88    /**
    9      * Post object for shared fixture.
    10      *
    11      * @var WP_Post
    12      */
    13     public static $post;
     9     * Array of posts.
     10     *
     11     * @var WP_Post[]
     12     */
     13    public static $posts;
     14
     15    /**
     16     * User IDs.
     17     *
     18     * Array of user IDs keyed by role.
     19     *
     20     * @var int[]
     21     */
     22    public static $user_ids;
    1423
    1524    public static function wpSetUpBeforeClass( $factory ) {
    16         self::make_user_by_role( 'administrator' );
    17         self::$post = $factory->post->create_and_get();
     25        self::$user_ids                     = array(
     26            'administrator' => self::make_user_by_role( 'administrator' ),
     27            'contributor'   => self::make_user_by_role( 'contributor' ),
     28        );
     29        self::$posts['publish']             = $factory->post->create_and_get();
     30        self::$posts['password']            = $factory->post->create_and_get(
     31            array(
     32                'post_password' => 'xmlrpc',
     33                'post_author'   => self::$user_ids['administrator'],
     34            )
     35        );
     36        self::$posts['private']             = $factory->post->create_and_get(
     37            array(
     38                'post_status' => 'private',
     39                'post_author' => self::$user_ids['administrator'],
     40            )
     41        );
     42        self::$posts['private_contributor'] = $factory->post->create_and_get(
     43            array(
     44                'post_status' => 'private',
     45                'post_author' => self::$user_ids['contributor'],
     46            )
     47        );
    1848    }
    1949
     
    2454                'administrator',
    2555                'administrator',
    26                 self::$post->ID,
     56                self::$posts['publish']->ID,
    2757                array(
    2858                    'content' => rand_str( 100 ),
     
    4070                'administrator',
    4171                'administrator',
    42                 self::$post->ID,
     72                self::$posts['publish']->ID,
    4373                array(
    4474                    'content' => '',
     
    6090                'administrator',
    6191                'administrator',
    62                 self::$post->ID,
     92                self::$posts['publish']->ID,
    6393                array(
    6494                    'content' => '   ',
     
    80110                'administrator',
    81111                'administrator',
    82                 self::$post->ID,
     112                self::$posts['publish']->ID,
    83113                array(
    84114                    'content' => '0',
     
    100130                'administrator',
    101131                'administrator',
    102                 self::$post->ID,
     132                self::$posts['publish']->ID,
    103133                array(
    104134                    'content' => '   ',
     
    140170            'administrator',
    141171            'administrator',
    142             self::$post->ID,
     172            self::$posts['publish']->ID,
    143173            array(
    144174                'content' => rand_str( 100 ),
     
    169199            '',
    170200            '',
    171             self::$post->ID,
     201            self::$posts['publish']->ID,
    172202            array(
    173203                'author'       => 'WordPress',
     
    194224            '',
    195225            '',
    196             self::$post->ID,
     226            self::$posts['publish']->ID,
    197227            array(
    198228                'author'       => 'WordPress',
     
    219249            'administrator',
    220250            'administrator',
    221             self::$post->ID,
     251            self::$posts['publish']->ID,
    222252            array(
    223253                'author'       => 'WordPress',
     
    233263        $this->assertSame( $user_id, (int) $comment->user_id );
    234264    }
     265
     266    /**
     267     * Ensure users can only comment on posts they're permitted to access.
     268     *
     269     * @dataProvider data_comments_observe_post_permissions
     270     *
     271     * @param string $post_key      Post identifier from the self::$posts array.
     272     * @param string $username      Username leaving comment.
     273     * @param bool   $expected      Expected result. True: successfull comment. False: Refused comment.
     274     * @param string $anon_callback Optional. Allow anonymous comment callback. Default __return_false.
     275     */
     276    function test_comments_observe_post_permissions( $post_key, $username, $expected, $anon_callback = '__return_false' ) {
     277        add_filter( 'xmlrpc_allow_anonymous_comments', $anon_callback );
     278
     279        $comment_args = array(
     280            1,
     281            $username,
     282            $username,
     283            self::$posts[ $post_key ]->ID,
     284            array(
     285                'author'       => 'WordPress',
     286                'author_email' => 'noreply@wordpress.org',
     287                'content'      => 'Test Comment',
     288            ),
     289        );
     290
     291        $result = $this->myxmlrpcserver->wp_newComment( $comment_args );
     292        if ( $expected ) {
     293            $this->assertInternalType( 'int', $result );
     294            return;
     295        }
     296
     297        $this->assertIXRError( $result );
     298        $this->assertSame( 403, $result->code );
     299    }
     300
     301    /**
     302     * Data provider for test_comments_observe_post_permissions.
     303     *
     304     * @return array[] {
     305     *     @type string Post identifier from the self::$posts array.
     306     *     @type string Username leaving comment.
     307     *     @type bool   Expected result. True: successfull comment. False: Refused comment.
     308     *     @type string Optional. Allow anonymous comment callback. Default __return_false.
     309     * }
     310     */
     311    function data_comments_observe_post_permissions() {
     312        return array(
     313            // 0: Post author, password protected public post.
     314            array(
     315                'password',
     316                'administrator',
     317                true,
     318            ),
     319            // 1: Low privileged non-author, password protected public post.
     320            array(
     321                'password',
     322                'contributor',
     323                false,
     324            ),
     325            // 2: Anonymous user, password protected public post.
     326            array(
     327                'password',
     328                '', // Anonymous user.
     329                false,
     330            ),
     331            // 3: Anonymous user, anon comments allowed, password protected public post.
     332            array(
     333                'password',
     334                '', // Anonymous user.
     335                false,
     336                '__return_true',
     337            ),
     338
     339            // 4: Post author, private post.
     340            array(
     341                'private',
     342                'administrator',
     343                true,
     344            ),
     345            // 5: Low privileged non-author, private post.
     346            array(
     347                'private',
     348                'contributor',
     349                false,
     350            ),
     351            // 6: Anonymous user, private post.
     352            array(
     353                'private',
     354                '', // Anonymous user.
     355                false,
     356            ),
     357            // 7: Anonymous user, anon comments allowed, private post.
     358            array(
     359                'private',
     360                '', // Anonymous user.
     361                false,
     362                '__return_true',
     363            ),
     364
     365            // 8: High privileged non-author, private post.
     366            array(
     367                'private_contributor',
     368                'administrator',
     369                true,
     370            ),
     371            // 9: Low privileged author, private post.
     372            array(
     373                'private_contributor',
     374                'contributor',
     375                true,
     376            ),
     377            // 10: Anonymous user, private post.
     378            array(
     379                'private_contributor',
     380                '', // Anonymous user.
     381                false,
     382            ),
     383            // 11: Anonymous user, anon comments allowed, private post.
     384            array(
     385                'private_contributor',
     386                '', // Anonymous user.
     387                false,
     388                '__return_true',
     389            ),
     390        );
     391    }
    235392}
Note: See TracChangeset for help on using the changeset viewer.