From 507cab8d6f5d469c6b0131bb07a45eb6f0c6724e Mon Sep 17 00:00:00 2001 From: "K. Adam White" Date: Tue, 27 Jun 2023 17:24:44 +0000 Subject: [PATCH] REST API: Check post meta update authorization only when value is changed. Resolves a bug where a post save will be reported as failed if the post includes any meta keys the current user does not have authorization to update, even when those meta values are unchanged. Write authorization is now checked for a meta key only when the value of that key has changed, so that passing a REST response back unchanged will not cause failures. Authorization is only needed when data will be updated. Props ckoerner, TimothyBlynJacobs, spacedmonkey git-svn-id: https://develop.svn.wordpress.org/trunk@56075 602fd350-edb4-49c9-b593-d223f7449a82 --- .../fields/class-wp-rest-meta-fields.php | 20 +++++------ .../tests/rest-api/rest-post-meta-fields.php | 36 +++++++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php b/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php index 1b6a7fe37f..60c3c3ab24 100644 --- a/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php +++ b/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php @@ -368,6 +368,16 @@ abstract class WP_REST_Meta_Fields { protected function update_meta_value( $object_id, $meta_key, $name, $value ) { $meta_type = $this->get_meta_type(); + // Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false. + $old_value = get_metadata( $meta_type, $object_id, $meta_key ); + $subtype = get_object_subtype( $meta_type, $object_id ); + + if ( is_array( $old_value ) && 1 === count( $old_value ) + && $this->is_meta_value_same_as_stored_value( $meta_key, $subtype, $old_value[0], $value ) + ) { + return true; + } + if ( ! current_user_can( "edit_{$meta_type}_meta", $object_id, $meta_key ) ) { return new WP_Error( 'rest_cannot_update', @@ -380,16 +390,6 @@ abstract class WP_REST_Meta_Fields { ); } - // Do the exact same check for a duplicate value as in update_metadata() to avoid update_metadata() returning false. - $old_value = get_metadata( $meta_type, $object_id, $meta_key ); - $subtype = get_object_subtype( $meta_type, $object_id ); - - if ( is_array( $old_value ) && 1 === count( $old_value ) - && $this->is_meta_value_same_as_stored_value( $meta_key, $subtype, $old_value[0], $value ) - ) { - return true; - } - if ( ! update_metadata( $meta_type, $object_id, wp_slash( $meta_key ), wp_slash( $value ) ) ) { return new WP_Error( 'rest_meta_database_error', diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index 3c627dfd59..2142e80582 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -2299,6 +2299,42 @@ class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { $this->assertSame( array( 'project' => 'WordCamp' ), $data['meta']['object'] ); } + /** + * @ticket 57745 + */ + public function test_update_meta_with_unchanged_values_and_custom_authentication() { + register_post_meta( + 'post', + 'authenticated', + array( + 'single' => true, + 'type' => 'boolean', + 'default' => false, + 'show_in_rest' => true, + 'auth_callback' => '__return_false', + ) + ); + + add_post_meta( self::$post_id, 'authenticated', false ); + + $this->grant_write_permission(); + + $request = new WP_REST_Request( 'PUT', sprintf( '/wp/v2/posts/%d', self::$post_id ) ); + $request->set_body_params( + array( + 'meta' => array( + 'authenticated' => false, + ), + ) + ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertSame( 200, $response->get_status() ); + + $data = $response->get_data(); + $this->assertSame( false, $data['meta']['authenticated'] ); + } + /** * @ticket 43392 */