Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patterns: update pattern category capabilities to prevent authors adding new categories #5510

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,60 @@ public function get_item_schema() {

return $this->add_additional_fields_schema( $this->schema );
}

/**
* Gets the link relations available for the post and current user.
*
* @since 6.4.0 Ensures that only users with `edit_terms` capability can add `wp_pattern_category`
* taxonomy terms.
*
* @param WP_Post $post Post object.
* @param WP_REST_Request $request Request object.
* @return array List of link relations.
*/
protected function get_available_actions( $post, $request ) {
if ( 'edit' !== $request['context'] ) {
return array();
}

$rels = array();

$post_type = get_post_type_object( $post->post_type );

if ( 'attachment' !== $this->post_type && current_user_can( $post_type->cap->publish_posts ) ) {
$rels[] = 'https://api.w.org/action-publish';
}

if ( current_user_can( 'unfiltered_html' ) ) {
$rels[] = 'https://api.w.org/action-unfiltered-html';
}

if ( 'post' === $post_type->name ) {
if ( current_user_can( $post_type->cap->edit_others_posts ) && current_user_can( $post_type->cap->publish_posts ) ) {
$rels[] = 'https://api.w.org/action-sticky';
}
}

if ( post_type_supports( $post_type->name, 'author' ) ) {
if ( current_user_can( $post_type->cap->edit_others_posts ) ) {
$rels[] = 'https://api.w.org/action-assign-author';
}
}

$taxonomies = wp_list_filter( get_object_taxonomies( $this->post_type, 'objects' ), array( 'show_in_rest' => true ) );

foreach ( $taxonomies as $tax ) {
$tax_base = ! empty( $tax->rest_base ) ? $tax->rest_base : $tax->name;

if ( current_user_can( $tax->cap->edit_terms ) ) {
$rels[] = 'https://api.w.org/action-create-' . $tax_base;
}

if ( current_user_can( $tax->cap->assign_terms ) ) {
$rels[] = 'https://api.w.org/action-assign-' . $tax_base;
}
}

return $rels;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
/**
* REST API: WP_REST_Pattern_Categories_Controller class
*
* @package WordPress
* @subpackage REST_API
* @since 6.4.0
*/

/**
* Core class used to managed permissions check for wp_pattern_category terms.
*
* @since 6.4.0
*
* @see WP_REST_Controller
*/
class WP_REST_Pattern_Categories_Controller extends WP_REST_Terms_Controller {
/**
* Make pattern categories behave more like a hierarchical taxonomy in terms of permissions.
* Check the edit_terms cap to see whether term creation is possible.
*
* @since 6.4.0
*
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function create_item_permissions_check( $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) {
return false;
}

$taxonomy_obj = get_taxonomy( $this->taxonomy );

// Patterns categories are a flat hierarchy (like tags), but work more like post categories in terms of permissions.
if ( ! current_user_can( $taxonomy_obj->cap->edit_terms ) ) {
return new WP_Error(
'rest_cannot_create',
__( 'Sorry, you are not allowed to create terms in this taxonomy.' ),
array( 'status' => rest_authorization_required_code() )
);
}

return true;
}
}
23 changes: 12 additions & 11 deletions src/wp-includes/taxonomy.php
Original file line number Diff line number Diff line change
Expand Up @@ -227,20 +227,21 @@ function create_initial_taxonomies() {
'wp_pattern_category',
array( 'wp_block' ),
array(
'public' => true,
'publicly_queryable' => false,
'hierarchical' => false,
'labels' => array(
'public' => true,
'publicly_queryable' => false,
'hierarchical' => false,
'labels' => array(
'name' => _x( 'Pattern Categories', 'taxonomy general name' ),
'singular_name' => _x( 'Pattern Category', 'taxonomy singular name' ),
),
'query_var' => false,
'rewrite' => false,
'show_ui' => true,
'_builtin' => true,
'show_in_nav_menus' => false,
'show_in_rest' => true,
'show_admin_column' => true,
'query_var' => false,
'rewrite' => false,
'show_ui' => true,
'_builtin' => true,
'show_in_nav_menus' => false,
'show_in_rest' => true,
'show_admin_column' => true,
'rest_controller_class' => 'WP_REST_Pattern_Categories_Controller',
)
);
}
Expand Down
1 change: 1 addition & 0 deletions src/wp-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-templates-controller.php';
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-url-details-controller.php';
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-navigation-fallback-controller.php';
require ABSPATH . WPINC . '/rest-api/endpoints/class-wp-rest-pattern-categories-controller.php';
require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-meta-fields.php';
require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-comment-meta-fields.php';
require ABSPATH . WPINC . '/rest-api/fields/class-wp-rest-post-meta-fields.php';
Expand Down
19 changes: 19 additions & 0 deletions tests/phpunit/tests/rest-api/rest-categories-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class WP_Test_REST_Categories_Controller extends WP_Test_REST_Controller_Testcas
protected static $administrator;
protected static $contributor;
protected static $subscriber;
protected static $author;

protected static $category_ids = array();
protected static $total_categories = 30;
Expand All @@ -33,6 +34,11 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
'role' => 'subscriber',
)
);
self::$author = $factory->user->create(
array(
'role' => 'author',
)
);

// Set up categories for pagination tests.
for ( $i = 0; $i < self::$total_categories - 1; $i++ ) {
Expand All @@ -47,6 +53,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
public static function wpTearDownAfterClass() {
self::delete_user( self::$administrator );
self::delete_user( self::$subscriber );
self::delete_user( self::$author );

// Remove categories for pagination tests.
foreach ( self::$category_ids as $category_id ) {
Expand Down Expand Up @@ -1178,6 +1185,18 @@ public function test_get_additional_field_registration() {
$wp_rest_additional_fields = array();
}

/**
* @ticket 59660
*/
public function test_create_item_pattern_category_incorrect_permissions_author() {
wp_set_current_user( self::$author );

$request = new WP_REST_Request( 'POST', '/wp/v2/wp_pattern_category' );
$request->set_param( 'name', 'Incorrect permissions' );
$response = rest_get_server()->dispatch( $request );
$this->assertErrorResponse( 'rest_cannot_create', $response, 403 );
}

public function additional_field_get_callback( $response_data, $field_name ) {
return 123;
}
Expand Down
Loading