From 973ade2a281f1c81c98d80208d9fc6272a0ba0b2 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Thu, 3 Nov 2016 03:15:28 +0000 Subject: [PATCH] REST API: Return a `WP_Error` when a user does not have permission to create or update a post with the provided terms. Add the 'assign_term' check for post create and update. Props boonebgorges, johnbillion. Fixes #38505. git-svn-id: https://develop.svn.wordpress.org/trunk@39108 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-posts-controller.php | 40 ++++++++++++++ .../tests/rest-api/rest-posts-controller.php | 53 +++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index dfa4b4e7b5..ce5d3d91b8 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -459,6 +459,10 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create new posts.' ), array( 'status' => rest_authorization_required_code() ) ); } + if ( ! $this->check_assign_terms_permission( $request ) ) { + return new WP_Error( 'rest_cannot_assign_term', __( 'You do not have permission to assign the provided terms.' ), array( 'status' => rest_authorization_required_code() ) ); + } + return true; } @@ -592,6 +596,10 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { return new WP_Error( 'rest_cannot_assign_sticky', __( 'You do not have permission to make posts sticky.' ), array( 'status' => rest_authorization_required_code() ) ); } + if ( ! $this->check_assign_terms_permission( $request ) ) { + return new WP_Error( 'rest_cannot_assign_term', __( 'You do not have permission to assign the provided terms.' ), array( 'status' => rest_authorization_required_code() ) ); + } + return true; } @@ -1205,6 +1213,38 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { } } + /** + * Checks whether current user can assign all terms sent with the current request. + * + * @since 4.7.0 + * + * @param WP_REST_Request $request The request object with post and terms data. + * @return bool Whether the current user can assign the provided terms. + */ + protected function check_assign_terms_permission( $request ) { + $taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) ); + foreach ( $taxonomies as $taxonomy ) { + $base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; + + if ( ! isset( $request[ $base ] ) ) { + continue; + } + + foreach ( $request[ $base ] as $term_id ) { + // Invalid terms will be rejected later. + if ( ! get_term( $term_id, $taxonomy->name ) ) { + continue; + } + + if ( ! current_user_can( 'assign_term', (int) $term_id ) ) { + return false; + } + } + } + + return true; + } + /** * Checks if a given post type can be viewed or managed. * diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php index 078f7aa57f..eff261c2f7 100644 --- a/tests/phpunit/tests/rest-api/rest-posts-controller.php +++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php @@ -18,6 +18,8 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te protected static $supported_formats; + protected $forbidden_cat; + public static function wpSetUpBeforeClass( $factory ) { self::$post_id = $factory->post->create(); @@ -1504,6 +1506,35 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te $this->assertEquals( array(), $data['categories'] ); } + /** + * @ticket 38505 + */ + public function test_create_post_with_categories_that_cannot_be_assigned_by_current_user() { + $cats = self::factory()->category->create_many( 2 ); + $this->forbidden_cat = $cats[1]; + + wp_set_current_user( self::$editor_id ); + $request = new WP_REST_Request( 'POST', '/wp/v2/posts' ); + $params = $this->set_post_data( array( + 'password' => 'testing', + 'categories' => $cats, + ) ); + $request->set_body_params( $params ); + + add_filter( 'map_meta_cap', array( $this, 'revoke_assign_term' ), 10, 4 ); + $response = $this->server->dispatch( $request ); + remove_filter( 'map_meta_cap', array( $this, 'revoke_assign_term' ), 10, 4 ); + + $this->assertErrorResponse( 'rest_cannot_assign_term', $response, 403 ); + } + + public function revoke_assign_term( $caps, $cap, $user_id, $args ) { + if ( 'assign_term' === $cap && isset( $args[0] ) && $this->forbidden_cat == $args[0] ) { + $caps = array( 'do_not_allow' ); + } + return $caps; + } + public function test_update_item() { wp_set_current_user( self::$editor_id ); @@ -1950,6 +1981,28 @@ class WP_Test_REST_Posts_Controller extends WP_Test_REST_Post_Type_Controller_Te $this->assertEquals( array(), $new_data['categories'] ); } + /** + * @ticket 38505 + */ + public function test_update_post_with_categories_that_cannot_be_assigned_by_current_user() { + $cats = self::factory()->category->create_many( 2 ); + $this->forbidden_cat = $cats[1]; + + wp_set_current_user( self::$editor_id ); + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) ); + $params = $this->set_post_data( array( + 'password' => 'testing', + 'categories' => $cats, + ) ); + $request->set_body_params( $params ); + + add_filter( 'map_meta_cap', array( $this, 'revoke_assign_term' ), 10, 4 ); + $response = $this->server->dispatch( $request ); + remove_filter( 'map_meta_cap', array( $this, 'revoke_assign_term' ), 10, 4 ); + + $this->assertErrorResponse( 'rest_cannot_assign_term', $response, 403 ); + } + public function test_delete_item() { $post_id = $this->factory->post->create( array( 'post_title' => 'Deleted post' ) ); wp_set_current_user( self::$editor_id );