Skip to content

Commit 34a07f9

Browse files
gzioloclaude
andcommitted
Content Guidelines Revisions: Lock revision deletion to administrators
The inherited WP_REST_Revisions_Controller::delete_item_permissions_check only requires `delete_post` capability, which editors hold on guideline posts. The singleton route is admin-managed for every other write — align revision deletion with that rule. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e0b67ee commit 34a07f9

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

‎lib/experimental/guidelines/class-gutenberg-content-guidelines-revisions-controller.php‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,33 @@ public function get_item_schema() {
228228
return $schema;
229229
}
230230

231+
/**
232+
* Restricts revision deletion to administrators.
233+
*
234+
* The inherited check only requires `delete_post` on the parent and the
235+
* revision. The singleton route is admin-managed for every other write,
236+
* so deleting revisions follows the same rule.
237+
*
238+
* @param WP_REST_Request $request Full details about the request.
239+
* @return true|WP_Error True if the request has access, WP_Error object otherwise.
240+
*/
241+
public function delete_item_permissions_check( $request ) {
242+
$parent = $this->get_parent( $request['parent'] );
243+
if ( is_wp_error( $parent ) ) {
244+
return $parent;
245+
}
246+
247+
if ( ! current_user_can( 'manage_options' ) ) {
248+
return new WP_Error(
249+
'rest_cannot_delete',
250+
__( 'Sorry, you are not allowed to delete revisions.', 'gutenberg' ),
251+
array( 'status' => rest_authorization_required_code() )
252+
);
253+
}
254+
255+
return parent::delete_item_permissions_check( $request );
256+
}
257+
231258
/**
232259
* Checks if a given request has access to restore a revision.
233260
*

‎phpunit/experimental/guidelines/class-gutenberg-content-guidelines-rest-controller-test.php‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,42 @@ public function test_restore_revision_response_includes_links() {
764764
$this->assertArrayHasKey( 'version-history', $links );
765765
}
766766

767+
/**
768+
* Editors cannot delete revisions on the singleton route. The inherited
769+
* check would allow it via `delete_post`. The override locks it to
770+
* administrators to match the rest of the singleton's write policy.
771+
*
772+
* @covers Gutenberg_Content_Guidelines_Revisions_Controller::delete_item_permissions_check
773+
*/
774+
public function test_delete_revision_no_permission_editor() {
775+
wp_set_current_user( self::$admin_id );
776+
$create_response = $this->create_guidelines();
777+
$post_id = $create_response->get_data()['id'];
778+
779+
$request = new WP_REST_Request( 'PATCH', self::REST_BASE . '/' . $post_id );
780+
$request->set_param(
781+
'guideline_categories',
782+
array( 'copy' => array( 'guidelines' => 'Updated.' ) )
783+
);
784+
rest_get_server()->dispatch( $request );
785+
786+
$request = new WP_REST_Request( 'GET', self::REST_BASE . '/' . $post_id . '/revisions' );
787+
$revisions = rest_get_server()->dispatch( $request )->get_data();
788+
789+
$this->assertNotEmpty( $revisions );
790+
791+
wp_set_current_user( self::$editor_id );
792+
793+
$request = new WP_REST_Request(
794+
'DELETE',
795+
self::REST_BASE . '/' . $post_id . '/revisions/' . $revisions[0]['id']
796+
);
797+
$request->set_param( 'force', true );
798+
$response = rest_get_server()->dispatch( $request );
799+
800+
$this->assertErrorResponse( 'rest_cannot_delete', $response, 403 );
801+
}
802+
767803
/**
768804
* Tests that restoring requires admin permissions.
769805
*

0 commit comments

Comments
 (0)