From 46c238ca787a51164b4a7652c6ccbfdba0be214d Mon Sep 17 00:00:00 2001 From: Gary Pendergast Date: Fri, 13 Jul 2018 04:23:35 +0000 Subject: [PATCH] REST API: Tweak permission checks for taxonomy and term endpoints To match behaviour in the Classic Editor, we need to slightly loosen permissions on taxonomy and term endpoints. This allows users to create terms to assign to a post that they're editing. Props danielbachhuber. Fixes #44096. git-svn-id: https://develop.svn.wordpress.org/trunk@43440 602fd350-edb4-49c9-b593-d223f7449a82 --- .../class-wp-rest-taxonomies-controller.php | 6 ++--- .../class-wp-rest-terms-controller.php | 5 ++++- .../rest-api/rest-categories-controller.php | 14 ++++++++++++ .../tests/rest-api/rest-tags-controller.php | 22 +++++++++++++++++++ .../rest-api/rest-taxonomies-controller.php | 17 ++++++++++++++ 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php index e431d4b743..32c543ce19 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php @@ -84,7 +84,7 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller { $taxonomies = get_taxonomies( '', 'objects' ); } foreach ( $taxonomies as $taxonomy ) { - if ( ! empty( $taxonomy->show_in_rest ) && current_user_can( $taxonomy->cap->manage_terms ) ) { + if ( ! empty( $taxonomy->show_in_rest ) && current_user_can( $taxonomy->cap->assign_terms ) ) { return true; } } @@ -113,7 +113,7 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller { } $data = array(); foreach ( $taxonomies as $tax_type => $value ) { - if ( empty( $value->show_in_rest ) || ( 'edit' === $request['context'] && ! current_user_can( $value->cap->manage_terms ) ) ) { + if ( empty( $value->show_in_rest ) || ( 'edit' === $request['context'] && ! current_user_can( $value->cap->assign_terms ) ) ) { continue; } $tax = $this->prepare_item_for_response( $value, $request ); @@ -145,7 +145,7 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller { if ( empty( $tax_obj->show_in_rest ) ) { return false; } - if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->manage_terms ) ) { + if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->assign_terms ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to manage terms in this taxonomy.' ), array( 'status' => rest_authorization_required_code() ) ); } } diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php index ea2d0d19a7..af806b812f 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php @@ -380,7 +380,10 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { } $taxonomy_obj = get_taxonomy( $this->taxonomy ); - if ( ! current_user_can( $taxonomy_obj->cap->edit_terms ) ) { + if ( ( is_taxonomy_hierarchical( $this->taxonomy ) + && ! current_user_can( $taxonomy_obj->cap->edit_terms ) ) + || ( ! is_taxonomy_hierarchical( $this->taxonomy ) + && ! current_user_can( $taxonomy_obj->cap->assign_terms ) ) ) { return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create new terms.' ), array( 'status' => rest_authorization_required_code() ) ); } diff --git a/tests/phpunit/tests/rest-api/rest-categories-controller.php b/tests/phpunit/tests/rest-api/rest-categories-controller.php index 2e01943158..ac8d02a0ea 100644 --- a/tests/phpunit/tests/rest-api/rest-categories-controller.php +++ b/tests/phpunit/tests/rest-api/rest-categories-controller.php @@ -12,6 +12,7 @@ */ class WP_Test_REST_Categories_Controller extends WP_Test_REST_Controller_Testcase { protected static $administrator; + protected static $contributor; protected static $subscriber; public static function wpSetUpBeforeClass( $factory ) { @@ -20,6 +21,11 @@ class WP_Test_REST_Categories_Controller extends WP_Test_REST_Controller_Testcas 'role' => 'administrator', ) ); + self::$contributor = $factory->user->create( + array( + 'role' => 'subscriber', + ) + ); self::$subscriber = $factory->user->create( array( 'role' => 'subscriber', @@ -726,6 +732,14 @@ class WP_Test_REST_Categories_Controller extends WP_Test_REST_Controller_Testcas $this->assertErrorResponse( 'rest_cannot_create', $response, 403 ); } + public function test_create_item_incorrect_permissions_contributor() { + wp_set_current_user( self::$contributor ); + $request = new WP_REST_Request( 'POST', '/wp/v2/categories' ); + $request->set_param( 'name', 'Incorrect permissions' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_create', $response, 403 ); + } + public function test_create_item_missing_arguments() { wp_set_current_user( self::$administrator ); $request = new WP_REST_Request( 'POST', '/wp/v2/categories' ); diff --git a/tests/phpunit/tests/rest-api/rest-tags-controller.php b/tests/phpunit/tests/rest-api/rest-tags-controller.php index f2cd6a74a8..2b3cd11250 100644 --- a/tests/phpunit/tests/rest-api/rest-tags-controller.php +++ b/tests/phpunit/tests/rest-api/rest-tags-controller.php @@ -13,6 +13,7 @@ class WP_Test_REST_Tags_Controller extends WP_Test_REST_Controller_Testcase { protected static $superadmin; protected static $administrator; protected static $editor; + protected static $contributor; protected static $subscriber; public static function wpSetUpBeforeClass( $factory ) { @@ -32,6 +33,11 @@ class WP_Test_REST_Tags_Controller extends WP_Test_REST_Controller_Testcase { 'role' => 'editor', ) ); + self::$contributor = $factory->user->create( + array( + 'role' => 'contributor', + ) + ); self::$subscriber = $factory->user->create( array( 'role' => 'subscriber', @@ -624,6 +630,22 @@ class WP_Test_REST_Tags_Controller extends WP_Test_REST_Controller_Testcase { $this->assertEquals( 'so-awesome', $data['slug'] ); } + public function test_create_item_contributor() { + wp_set_current_user( self::$contributor ); + $request = new WP_REST_Request( 'POST', '/wp/v2/tags' ); + $request->set_param( 'name', 'My Awesome Term' ); + $request->set_param( 'description', 'This term is so awesome.' ); + $request->set_param( 'slug', 'so-awesome' ); + $response = rest_get_server()->dispatch( $request ); + $this->assertEquals( 201, $response->get_status() ); + $headers = $response->get_headers(); + $data = $response->get_data(); + $this->assertContains( '/wp/v2/tags/' . $data['id'], $headers['Location'] ); + $this->assertEquals( 'My Awesome Term', $data['name'] ); + $this->assertEquals( 'This term is so awesome.', $data['description'] ); + $this->assertEquals( 'so-awesome', $data['slug'] ); + } + public function test_create_item_incorrect_permissions() { wp_set_current_user( self::$subscriber ); $request = new WP_REST_Request( 'POST', '/wp/v2/tags' ); diff --git a/tests/phpunit/tests/rest-api/rest-taxonomies-controller.php b/tests/phpunit/tests/rest-api/rest-taxonomies-controller.php index 635969cb38..3eea3be489 100644 --- a/tests/phpunit/tests/rest-api/rest-taxonomies-controller.php +++ b/tests/phpunit/tests/rest-api/rest-taxonomies-controller.php @@ -62,6 +62,23 @@ class WP_Test_REST_Taxonomies_Controller extends WP_Test_REST_Controller_Testcas $this->assertEquals( 'tags', $data['post_tag']['rest_base'] ); } + public function test_get_items_context_edit() { + wp_set_current_user( self::$contributor_id ); + $request = new WP_REST_Request( 'GET', '/wp/v2/taxonomies' ); + $request->set_param( 'context', 'edit' ); + $response = rest_get_server()->dispatch( $request ); + $data = $response->get_data(); + $taxonomies = $this->get_public_taxonomies( get_taxonomies( '', 'objects' ) ); + $this->assertEquals( count( $taxonomies ), count( $data ) ); + $this->assertEquals( 'Categories', $data['category']['name'] ); + $this->assertEquals( 'category', $data['category']['slug'] ); + $this->assertEquals( true, $data['category']['hierarchical'] ); + $this->assertEquals( 'Tags', $data['post_tag']['name'] ); + $this->assertEquals( 'post_tag', $data['post_tag']['slug'] ); + $this->assertEquals( false, $data['post_tag']['hierarchical'] ); + $this->assertEquals( 'tags', $data['post_tag']['rest_base'] ); + } + public function test_get_items_invalid_permission_for_context() { wp_set_current_user( 0 ); $request = new WP_REST_Request( 'GET', '/wp/v2/taxonomies' );