Make WordPress Core

Changeset 47809


Ignore:
Timestamp:
05/16/2020 06:41:41 PM (6 years ago)
Author:
TimothyBlynJacobs
Message:

REST API: Check required properties are provided when validating an object.

Previously, the WP_REST_Request object validated that top-level properties were defined, but this did not extend to those object's required properties. This adds validation to rest_validate_value_from_schema() directly.

Both the v3 and v4 JSON Schema syntax for required properties is supported.

Props sorenbronsted.
Fixes #48818.

Location:
trunk
Files:
2 edited

Legend:

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

    r47758 r47809  
    12831283                        /* translators: 1: Parameter, 2: Type name. */
    12841284                        return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'object' ) );
     1285                }
     1286
     1287                if ( isset( $args['required'] ) && is_array( $args['required'] ) ) { // schema version 4
     1288                        foreach ( $args['required'] as $name ) {
     1289                                if ( ! array_key_exists( $name, $value ) ) {
     1290                                        /* translators: 1: Property of an object, 2: Parameter. */
     1291                                        return new WP_Error( 'rest_property_required', sprintf( __( '%1$s is a required property of %2$s.' ), $name, $param ) );
     1292                                }
     1293                        }
     1294                } elseif ( isset( $args['properties'] ) ) { // schema version 3
     1295                        foreach ( $args['properties'] as $name => $property ) {
     1296                                if ( isset( $property['required'] ) && true === $property['required'] && ! array_key_exists( $name, $value ) ) {
     1297                                        /* translators: 1: Property of an object, 2: Parameter. */
     1298                                        return new WP_Error( 'rest_property_required', sprintf( __( '%1$s is a required property of %2$s.' ), $name, $param ) );
     1299                                }
     1300                        }
    12851301                }
    12861302
  • trunk/tests/phpunit/tests/rest-api/rest-schema-validation.php

    r47753 r47809  
    33 * Unit tests covering schema validation and sanitization functionality.
    44 *
    5  * @package WordPress
     5 * @package    WordPress
    66 * @subpackage REST API
    77 */
    … …  
    410410                $this->assertWPError( rest_validate_value_from_schema( $mb_char . $mb_char . $mb_char, $schema ) );
    411411        }
     412
     413        /**
     414         * @ticket       48818
     415         * @dataProvider data_required_property
     416         */
     417        public function test_property_is_required( $data, $expected ) {
     418                $schema = array(
     419                        'type'       => 'object',
     420                        'properties' => array(
     421                                'my_prop'          => array(
     422                                        'type' => 'string',
     423                                ),
     424                                'my_required_prop' => array(
     425                                        'type'     => 'string',
     426                                        'required' => true,
     427                                ),
     428                        ),
     429                );
     430
     431                $valid = rest_validate_value_from_schema( $data, $schema );
     432
     433                if ( $expected ) {
     434                        $this->assertTrue( $valid );
     435                } else {
     436                        $this->assertWPError( $valid );
     437                }
     438        }
     439
     440        /**
     441         * @ticket       48818
     442         * @dataProvider data_required_property
     443         */
     444        public function test_property_is_required_v4( $data, $expected ) {
     445                $schema = array(
     446                        'type'       => 'object',
     447                        'properties' => array(
     448                                'my_prop'          => array(
     449                                        'type' => 'string',
     450                                ),
     451                                'my_required_prop' => array(
     452                                        'type' => 'string',
     453                                ),
     454                        ),
     455                        'required'   => array( 'my_required_prop' ),
     456                );
     457
     458                $valid = rest_validate_value_from_schema( $data, $schema );
     459
     460                if ( $expected ) {
     461                        $this->assertTrue( $valid );
     462                } else {
     463                        $this->assertWPError( $valid );
     464                }
     465        }
     466
     467        public function data_required_property() {
     468                return array(
     469                        array(
     470                                array(
     471                                        'my_required_prop' => 'test',
     472                                        'my_prop'          => 'test',
     473                                ),
     474                                true,
     475                        ),
     476                        array( array( 'my_prop' => 'test' ), false ),
     477                        array( array(), false ),
     478                );
     479        }
     480
     481        /**
     482         * @ticket       48818
     483         * @dataProvider data_required_nested_property
     484         */
     485        public function test_nested_property_is_required( $data, $expected ) {
     486                $schema = array(
     487                        'type'       => 'object',
     488                        'properties' => array(
     489                                'my_object' => array(
     490                                        'type'       => 'object',
     491                                        'properties' => array(
     492                                                'my_nested_prop'          => array(
     493                                                        'type' => 'string',
     494                                                ),
     495                                                'my_required_nested_prop' => array(
     496                                                        'type'     => 'string',
     497                                                        'required' => true,
     498                                                ),
     499                                        ),
     500                                ),
     501                        ),
     502                );
     503
     504                $valid = rest_validate_value_from_schema( $data, $schema );
     505
     506                if ( $expected ) {
     507                        $this->assertTrue( $valid );
     508                } else {
     509                        $this->assertWPError( $valid );
     510                }
     511        }
     512
     513        /**
     514         * @ticket       48818
     515         * @dataProvider data_required_nested_property
     516         */
     517        public function test_nested_property_is_required_v4( $data, $expected ) {
     518                $schema = array(
     519                        'type'       => 'object',
     520                        'properties' => array(
     521                                'my_object' => array(
     522                                        'type'       => 'object',
     523                                        'properties' => array(
     524                                                'my_nested_prop'          => array(
     525                                                        'type' => 'string',
     526                                                ),
     527                                                'my_required_nested_prop' => array(
     528                                                        'type' => 'string',
     529                                                ),
     530                                        ),
     531                                        'required'   => array( 'my_required_nested_prop' ),
     532                                ),
     533                        ),
     534                );
     535
     536                $valid = rest_validate_value_from_schema( $data, $schema );
     537
     538                if ( $expected ) {
     539                        $this->assertTrue( $valid );
     540                } else {
     541                        $this->assertWPError( $valid );
     542                }
     543        }
     544
     545        public function data_required_nested_property() {
     546                return array(
     547                        array(
     548                                array(
     549                                        'my_object' => array(
     550                                                'my_required_nested_prop' => 'test',
     551                                                'my_nested_prop'          => 'test',
     552                                        ),
     553                                ),
     554                                true,
     555                        ),
     556                        array(
     557                                array(
     558                                        'my_object' => array(
     559                                                'my_nested_prop' => 'test',
     560                                        ),
     561                                ),
     562                                false,
     563                        ),
     564                        array(
     565                                array(),
     566                                true,
     567                        ),
     568                );
     569        }
     570
     571        /**
     572         * @ticket       48818
     573         * @dataProvider data_required_deeply_nested_property
     574         */
     575        public function test_deeply_nested_v3_required_property( $value, $expected ) {
     576                $schema = array(
     577                        'type'       => 'object',
     578                        'properties' => array(
     579                                'propA' => array(
     580                                        'type'       => 'object',
     581                                        'required'   => true,
     582                                        'properties' => array(
     583                                                'propB' => array(
     584                                                        'type'       => 'object',
     585                                                        'required'   => true,
     586                                                        'properties' => array(
     587                                                                'propC' => array(
     588                                                                        'type'     => 'string',
     589                                                                        'required' => true,
     590                                                                ),
     591                                                                'propD' => array(
     592                                                                        'type' => 'string',
     593                                                                ),
     594                                                        ),
     595                                                ),
     596                                        ),
     597                                ),
     598                        ),
     599                );
     600
     601                $valid = rest_validate_value_from_schema( $value, $schema );
     602
     603                if ( $expected ) {
     604                        $this->assertTrue( $valid );
     605                } else {
     606                        $this->assertWPError( $valid );
     607                }
     608        }
     609
     610        /**
     611         * @ticket       48818
     612         * @dataProvider data_required_deeply_nested_property
     613         */
     614        public function test_deeply_nested_v4_required_property( $value, $expected ) {
     615                $schema = array(
     616                        'type'       => 'object',
     617                        'required'   => array( 'propA' ),
     618                        'properties' => array(
     619                                'propA' => array(
     620                                        'type'       => 'object',
     621                                        'required'   => array( 'propB' ),
     622                                        'properties' => array(
     623                                                'propB' => array(
     624                                                        'type'       => 'object',
     625                                                        'required'   => array( 'propC' ),
     626                                                        'properties' => array(
     627                                                                'propC' => array(
     628                                                                        'type' => 'string',
     629                                                                ),
     630                                                                'propD' => array(
     631                                                                        'type' => 'string',
     632                                                                ),
     633                                                        ),
     634                                                ),
     635                                        ),
     636                                ),
     637                        ),
     638                );
     639
     640                $valid = rest_validate_value_from_schema( $value, $schema );
     641
     642                if ( $expected ) {
     643                        $this->assertTrue( $valid );
     644                } else {
     645                        $this->assertWPError( $valid );
     646                }
     647        }
     648
     649        /**
     650         * @ticket       48818
     651         * @dataProvider data_required_deeply_nested_property
     652         */
     653        public function test_deeply_nested_mixed_version_required_property( $value, $expected ) {
     654                $schema = array(
     655                        'type'       => 'object',
     656                        'required'   => array( 'propA' ),
     657                        'properties' => array(
     658                                'propA' => array(
     659                                        'type'       => 'object',
     660                                        'required'   => array( 'propB' ),
     661                                        'properties' => array(
     662                                                'propB' => array(
     663                                                        'type'       => 'object',
     664                                                        'properties' => array(
     665                                                                'propC' => array(
     666                                                                        'type'     => 'string',
     667                                                                        'required' => true,
     668                                                                ),
     669                                                                'propD' => array(
     670                                                                        'type' => 'string',
     671                                                                ),
     672                                                        ),
     673                                                ),
     674                                        ),
     675                                ),
     676                        ),
     677                );
     678
     679                $valid = rest_validate_value_from_schema( $value, $schema );
     680
     681                if ( $expected ) {
     682                        $this->assertTrue( $valid );
     683                } else {
     684                        $this->assertWPError( $valid );
     685                }
     686        }
     687
     688        public function data_required_deeply_nested_property() {
     689                return array(
     690                        array(
     691                                array(),
     692                                false,
     693                        ),
     694                        array(
     695                                array(
     696                                        'propA' => array(),
     697                                ),
     698                                false,
     699                        ),
     700                        array(
     701                                array(
     702                                        'propA' => array(
     703                                                'propB' => array(),
     704                                        ),
     705                                ),
     706                                false,
     707                        ),
     708                        array(
     709                                array(
     710                                        'propA' => array(
     711                                                'propB' => array(
     712                                                        'propD' => 'd',
     713                                                ),
     714                                        ),
     715                                ),
     716                                false,
     717                        ),
     718                        array(
     719                                array(
     720                                        'propA' => array(
     721                                                'propB' => array(
     722                                                        'propC' => 'c',
     723                                                ),
     724                                        ),
     725                                ),
     726                                true,
     727                        ),
     728                );
     729        }
    412730}
Note: See TracChangeset for help on using the changeset viewer.