Make WordPress Core


Ignore:
Timestamp:
09/17/2024 09:50:38 PM (2 years ago)
Author:
TimothyBlynJacobs
Message:

REST API: Automatically populate targetHints for the Allow header.

The REST API uses the "Allow" header to communicate what methods a user is authorized to perform on a resource. This works great when operating on a single item route, but can break down when needing to determine authorization over a collection of items.

This commit uses the "targetHints" property of JSON Hyper Schema to provide access to the "allow" header for "self" links. This alleviates needing to make a separate network request for each item in a collection.

Props mamaduka, noisysocks, peterwilsoncc, spacedmonkey, swissspidy, timothyblynjacobs, tyxla, youknowriad.
Fixes #61739.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/rest-api/rest-server.php

    r57987 r59032  
    1010class Tests_REST_Server extends WP_Test_REST_TestCase {
    1111        protected static $icon_id;
     12        protected static $admin_id;
     13        protected static $post_id;
    1214
    1315        /**
    … …  
    2224
    2325        public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
    24                 $filename      = DIR_TESTDATA . '/images/test-image-large.jpg';
    25                 self::$icon_id = $factory->attachment->create_upload_object( $filename );
     26                $filename       = DIR_TESTDATA . '/images/test-image-large.jpg';
     27                self::$icon_id  = $factory->attachment->create_upload_object( $filename );
     28                self::$admin_id = $factory->user->create(
     29                        array(
     30                                'role' => 'administrator',
     31                        )
     32                );
     33                self::$post_id  = $factory->post->create();
    2634        }
    2735
    2836        public static function tear_down_after_class() {
    2937                wp_delete_attachment( self::$icon_id, true );
     38                self::delete_user( self::$admin_id );
     39                wp_delete_post( self::$post_id );
    3040
    3141                parent::tear_down_after_class();
    … …  
    24322442        }
    24332443
     2444        /**
     2445         * @ticket 61739
     2446         */
     2447        public function test_validates_request_when_building_target_hints() {
     2448                register_rest_route(
     2449                        'test-ns/v1',
     2450                        '/test/(?P<id>\d+)',
     2451                        array(
     2452                                array(
     2453                                        'methods'             => \WP_REST_Server::READABLE,
     2454                                        'callback'            => static function () {
     2455                                                return new \WP_REST_Response();
     2456                                        },
     2457                                        'permission_callback' => '__return_true',
     2458                                        'args'                => array(
     2459                                                'id' => array(
     2460                                                        'type' => 'integer',
     2461                                                ),
     2462                                        ),
     2463                                ),
     2464                        )
     2465                );
     2466
     2467                $response = new WP_REST_Response();
     2468                $response->add_link( 'self', rest_url( 'test-ns/v1/test/garbage' ) );
     2469
     2470                $links = rest_get_server()::get_response_links( $response );
     2471
     2472                $this->assertArrayHasKey( 'self', $links );
     2473                $this->assertArrayNotHasKey( 'targetHints', $links['self'][0] );
     2474        }
     2475
     2476        /**
     2477         * @ticket 61739
     2478         */
     2479        public function test_sanitizes_request_when_building_target_hints() {
     2480                $validated_param = null;
     2481                register_rest_route(
     2482                        'test-ns/v1',
     2483                        '/test/(?P<id>\d+)',
     2484                        array(
     2485                                array(
     2486                                        'methods'             => \WP_REST_Server::READABLE,
     2487                                        'callback'            => static function () {
     2488                                                return new \WP_REST_Response();
     2489                                        },
     2490                                        'permission_callback' => function ( WP_REST_Request $request ) use ( &$validated_param ) {
     2491                                                $validated_param = $request['id'];
     2492
     2493                                                return true;
     2494                                        },
     2495                                        'args'                => array(
     2496                                                'id' => array(
     2497                                                        'type' => 'integer',
     2498                                                ),
     2499                                        ),
     2500                                ),
     2501                        )
     2502                );
     2503
     2504                $response = new WP_REST_Response();
     2505                $response->add_link( 'self', rest_url( 'test-ns/v1/test/5' ) );
     2506
     2507                $links = rest_get_server()::get_response_links( $response );
     2508
     2509                $this->assertArrayHasKey( 'self', $links );
     2510                $this->assertArrayHasKey( 'targetHints', $links['self'][0] );
     2511                $this->assertIsInt( $validated_param );
     2512        }
     2513
     2514        /**
     2515         * @ticket 61739
     2516         */
     2517        public function test_populates_target_hints_for_administrator() {
     2518                wp_set_current_user( self::$admin_id );
     2519                $response = rest_do_request( '/wp/v2/posts' );
     2520                $post     = $response->get_data()[0];
     2521
     2522                $link = $post['_links']['self'][0];
     2523                $this->assertArrayHasKey( 'targetHints', $link );
     2524                $this->assertArrayHasKey( 'allow', $link['targetHints'] );
     2525                $this->assertSame( array( 'GET', 'POST', 'PUT', 'PATCH', 'DELETE' ), $link['targetHints']['allow'] );
     2526        }
     2527
     2528        /**
     2529         * @ticket 61739
     2530         */
     2531        public function test_populates_target_hints_for_logged_out_user() {
     2532                $response = rest_do_request( '/wp/v2/posts' );
     2533                $post     = $response->get_data()[0];
     2534
     2535                $link = $post['_links']['self'][0];
     2536                $this->assertArrayHasKey( 'targetHints', $link );
     2537                $this->assertArrayHasKey( 'allow', $link['targetHints'] );
     2538                $this->assertSame( array( 'GET' ), $link['targetHints']['allow'] );
     2539        }
     2540
     2541        /**
     2542         * @ticket 61739
     2543         */
     2544        public function test_does_not_error_on_invalid_urls() {
     2545                $response = new WP_REST_Response();
     2546                $response->add_link( 'self', 'this is not a real URL' );
     2547
     2548                $links = rest_get_server()::get_response_links( $response );
     2549                $this->assertArrayNotHasKey( 'targetHints', $links['self'][0] );
     2550        }
     2551
     2552        /**
     2553         * @ticket 61739
     2554         */
     2555        public function test_does_not_error_on_bad_rest_api_routes() {
     2556                $response = new WP_REST_Response();
     2557                $response->add_link( 'self', rest_url( '/this/is/not/a/real/route' ) );
     2558
     2559                $links = rest_get_server()::get_response_links( $response );
     2560                $this->assertArrayNotHasKey( 'targetHints', $links['self'][0] );
     2561        }
     2562
     2563        /**
     2564         * @ticket 61739
     2565         */
     2566        public function test_prefers_developer_defined_target_hints() {
     2567                $response = new WP_REST_Response();
     2568                $response->add_link(
     2569                        'self',
     2570                        '/wp/v2/posts/' . self::$post_id,
     2571                        array(
     2572                                'targetHints' => array(
     2573                                        'allow' => array( 'GET', 'PUT' ),
     2574                                ),
     2575                        )
     2576                );
     2577
     2578                $links = rest_get_server()::get_response_links( $response );
     2579                $link  = $links['self'][0];
     2580                $this->assertArrayHasKey( 'targetHints', $link );
     2581                $this->assertArrayHasKey( 'allow', $link['targetHints'] );
     2582                $this->assertSame( array( 'GET', 'PUT' ), $link['targetHints']['allow'] );
     2583        }
     2584
    24342585        public function _validate_as_integer_123( $value, $request, $key ) {
    24352586                if ( ! is_int( $value ) ) {
Note: See TracChangeset for help on using the changeset viewer.