Skip to content

Commit a8e672b

Browse files
pbkingclaude
andauthored
Fix #31: Use consistent custom capabilities throughout codebase (#35)
Updates all capability checks to use custom capabilities instead of generic WordPress capabilities for better security and consistency. Changes: - REST API permission callbacks now use `read_tbell_pattern_block` and `edit_tbell_pattern_blocks` - Controller methods add proper capability checks for theme and user pattern operations - Theme operations require `edit_theme_options` capability - User pattern operations require appropriate `tbell_pattern_block` capabilities - Test setup updated to properly initialize custom capabilities This provides granular control over pattern permissions and follows the principle of least privilege. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 289c5a7 commit a8e672b

3 files changed

Lines changed: 63 additions & 6 deletions

File tree

includes/class-pattern-builder-api.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,25 +63,25 @@ public function register_routes(): void {
6363

6464
/**
6565
* Permission callback for read operations.
66-
* Allows access to all logged-in users who can edit posts.
66+
* Allows access to users who can read pattern blocks.
6767
*
6868
* @return bool True if the user can read patterns, false otherwise.
6969
*/
7070
public function read_permission_callback() {
71-
return current_user_can( 'edit_posts' );
71+
return current_user_can( 'read_tbell_pattern_block' );
7272
}
7373

7474
/**
7575
* Permission callback for write operations (PUT, POST, DELETE).
76-
* Restricts access to administrators and editors only.
76+
* Restricts access to users with pattern editing capabilities.
7777
* Also verifies the REST API nonce for additional security.
7878
*
7979
* @param WP_REST_Request $request The REST request object.
8080
* @return bool|WP_Error True if the user can modify patterns, WP_Error otherwise.
8181
*/
8282
public function write_permission_callback( $request ) {
8383
// First check if user has the required capability
84-
if ( ! current_user_can( 'edit_others_posts' ) ) {
84+
if ( ! current_user_can( 'edit_tbell_pattern_blocks' ) ) {
8585
return new WP_Error(
8686
'rest_forbidden',
8787
__( 'You do not have permission to modify patterns.', 'pattern-builder' ),
@@ -421,7 +421,7 @@ function handle_hijack_block_update( $response, $handler, $request ) {
421421
if ( $post->post_type === 'tbell_pattern_block' || $convert_user_pattern_to_theme_pattern ) {
422422

423423
// Check write permissions before allowing update
424-
if ( ! current_user_can( 'edit_others_posts' ) ) {
424+
if ( ! current_user_can( 'edit_tbell_pattern_blocks' ) ) {
425425
return new WP_Error(
426426
'rest_forbidden',
427427
__( 'You do not have permission to edit patterns.', 'pattern-builder' ),

includes/class-pattern-builder-controller.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,15 @@ public function create_tbell_pattern_block_post_for_pattern( $pattern ) {
132132
}
133133

134134
public function update_theme_pattern( Abstract_Pattern $pattern, $options = array() ) {
135+
// Check if user has permission to modify theme patterns
136+
if ( ! current_user_can( 'edit_theme_options' ) ) {
137+
return new WP_Error(
138+
'insufficient_permissions',
139+
__( 'You do not have permission to modify theme patterns.', 'pattern-builder' ),
140+
array( 'status' => 403 )
141+
);
142+
}
143+
135144
// get the tbell_pattern_block post if it already exists
136145
$post = get_page_by_path( $this->format_pattern_slug_for_post( $pattern->name ), OBJECT, array( 'tbell_pattern_block', 'wp_block' ) );
137146

@@ -430,6 +439,14 @@ function ( $matches ) use ( $download_and_save_image ) {
430439
* @return Abstract_Pattern|WP_Error
431440
*/
432441
public function update_user_pattern( Abstract_Pattern $pattern ) {
442+
// Check if user has permission to edit pattern blocks
443+
if ( ! current_user_can( 'edit_tbell_pattern_blocks' ) ) {
444+
return new WP_Error(
445+
'insufficient_permissions',
446+
__( 'You do not have permission to modify patterns.', 'pattern-builder' ),
447+
array( 'status' => 403 )
448+
);
449+
}
433450
$post = get_page_by_path( $pattern->name, OBJECT, 'wp_block' );
434451
$convert_from_theme_pattern = false;
435452

@@ -527,6 +544,15 @@ public function get_block_patterns_from_database(): array {
527544
}
528545

529546
public function delete_user_pattern( Abstract_Pattern $pattern ) {
547+
// Check if user has permission to delete pattern blocks
548+
if ( ! current_user_can( 'delete_tbell_pattern_blocks' ) ) {
549+
return new WP_Error(
550+
'insufficient_permissions',
551+
__( 'You do not have permission to delete patterns.', 'pattern-builder' ),
552+
array( 'status' => 403 )
553+
);
554+
}
555+
530556
$post = get_page_by_path( $pattern->name, OBJECT, 'wp_block' );
531557

532558
if ( empty( $post ) ) {
@@ -575,8 +601,16 @@ function ( $p ) use ( $pattern ) {
575601
}
576602

577603
public function delete_theme_pattern( Abstract_Pattern $pattern ) {
604+
// Check if user has permission to modify theme patterns
605+
if ( ! current_user_can( 'edit_theme_options' ) ) {
606+
return new WP_Error(
607+
'insufficient_permissions',
608+
__( 'You do not have permission to delete theme patterns.', 'pattern-builder' ),
609+
array( 'status' => 403 )
610+
);
611+
}
578612

579-
$path = $this->get_pattern_filepath( $pattern );
613+
$path = $this->get_pattern_filepath( $pattern );
580614

581615
if ( ! $path ) {
582616
return new WP_Error( 'pattern_not_found', 'Pattern not found', array( 'status' => 404 ) );

tests/php/test-pattern-builder-api.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,21 @@ public function setUp(): void {
1313
$admin_id = self::factory()->user->create(['role' => 'administrator']);
1414
wp_set_current_user($admin_id);
1515

16+
// Initialize the post type class to set up custom capabilities
17+
new \TwentyBellows\PatternBuilder\Pattern_Builder_Post_Type();
18+
do_action('init');
19+
20+
// Directly grant capabilities to the test user
21+
$admin_user = new WP_User($admin_id);
22+
$admin_user->add_cap('read_tbell_pattern_block');
23+
$admin_user->add_cap('edit_tbell_pattern_blocks');
24+
$admin_user->add_cap('delete_tbell_pattern_blocks');
25+
26+
// Refresh current user to pick up new capabilities
27+
wp_cache_delete( $admin_id, 'users' );
28+
wp_cache_delete( $admin_id, 'user_meta' );
29+
wp_set_current_user( $admin_id );
30+
1631
// Create a temporary directory for the test patterns
1732
$this->test_dir = sys_get_temp_dir() . '/pattern-builder-test';
1833
$this->remove_test_directory($this->test_dir);
@@ -32,6 +47,14 @@ public function setUp(): void {
3247
* Clean up after each test
3348
*/
3449
public function tearDown(): void {
50+
// Clean up custom capabilities from administrator role
51+
$admin_role = get_role('administrator');
52+
if ($admin_role) {
53+
$admin_role->remove_cap('read_tbell_pattern_block');
54+
$admin_role->remove_cap('edit_tbell_pattern_blocks');
55+
$admin_role->remove_cap('delete_tbell_pattern_blocks');
56+
}
57+
3558
$this->remove_test_directory($this->test_dir);
3659
remove_filter('stylesheet_directory', [$this, 'get_test_directory']);
3760
parent::tearDown();

0 commit comments

Comments
 (0)