Make WordPress Core

Changeset 53408

Timestamp:
05/17/2022 06:59:24 PM (2 years ago)
Author:
SergeyBiryukov
Message:

Users: Fail gracefully when checking mapped capabilities without providing the required object ID.

This avoids an Undefined array key 0 PHP warning for current_user_can() capability checks that require a specific object to check against but an object ID was not passed.

A _doing_it_wrong() notice is also added, so that developers and site administrators are aware that the capability mapping is failing in the absence of the required object ID.

The list of mapped capabilities that require an object ID:

  • delete_post / delete_page
  • edit_post / edit_page
  • read_post / read_page
  • publish_post
  • edit_(post|comment|term|user)_meta / delete_*_meta / add_*_meta
  • edit_comment
  • edit_term / delete_term / assign_term

Follow-up to [34091], [34113], [47178].

Props jeherve, peterwilsoncc, henry.wright, johnbillion, mattheweppelsheimer, hellofromTonya, JeffPaul, azouamauriac, Ninos Ego, TobiasBg, wpsmith, GaryJ, nacin, johnstonphilip, azaozz, SergeyBiryukov.
Fixes #44591.

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/capabilities.php

    r53249 r53408  
    7474        case 'delete_post':
    7575        case 'delete_page':
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
    7695            $post = get_post( $args[0] );
    7796            if ( ! $post ) {
     
    93112            if ( ! $post_type ) {
    94113                /* translators: 1: Post type, 2: Capability name. */
    95                 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
     114                $message = __( 'The post type %1$s is not registered, so it may not be reliable to check the capability %2$s against a post of that type.' );
     115
     116                _doing_it_wrong(
     117                    __FUNCTION__,
     118                    sprintf(
     119                        $message,
     120                        '<code>' . $post->post_type . '</code>',
     121                        '<code>' . $cap . '</code>'
     122                    ),
     123                    '4.4.0'
     124                );
     125
    96126                $caps[] = 'edit_others_posts';
    97127                break;
     
    147177        case 'edit_post':
    148178        case 'edit_page':
     179
     180
     181
     182
     183
     184
     185
     186
     187
     188
     189
     190
     191
     192
     193
     194
     195
     196
     197
    149198            $post = get_post( $args[0] );
    150199            if ( ! $post ) {
     
    164213            if ( ! $post_type ) {
    165214                /* translators: 1: Post type, 2: Capability name. */
    166                 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
     215                $message = __( 'The post type %1$s is not registered, so it may not be reliable to check the capability %2$s against a post of that type.' );
     216
     217                _doing_it_wrong(
     218                    __FUNCTION__,
     219                    sprintf(
     220                        $message,
     221                        '<code>' . $post->post_type . '</code>',
     222                        '<code>' . $cap . '</code>'
     223                    ),
     224                    '4.4.0'
     225                );
     226
    167227                $caps[] = 'edit_others_posts';
    168228                break;
     
    216276        case 'read_post':
    217277        case 'read_page':
     278
     279
     280
     281
     282
     283
     284
     285
     286
     287
     288
     289
     290
     291
     292
     293
     294
     295
     296
    218297            $post = get_post( $args[0] );
    219298            if ( ! $post ) {
     
    233312            if ( ! $post_type ) {
    234313                /* translators: 1: Post type, 2: Capability name. */
    235                 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
     314                $message = __( 'The post type %1$s is not registered, so it may not be reliable to check the capability %2$s against a post of that type.' );
     315
     316                _doing_it_wrong(
     317                    __FUNCTION__,
     318                    sprintf(
     319                        $message,
     320                        '<code>' . $post->post_type . '</code>',
     321                        '<code>' . $cap . '</code>'
     322                    ),
     323                    '4.4.0'
     324                );
     325
    236326                $caps[] = 'edit_others_posts';
    237327                break;
     
    250340            if ( ! $status_obj ) {
    251341                /* translators: 1: Post status, 2: Capability name. */
    252                 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post status %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post with that status.' ), get_post_status( $post ), $cap ), '5.4.0' );
     342                $message = __( 'The post status %1$s is not registered, so it may not be reliable to check the capability %2$s against a post with that status.' );
     343
     344                _doing_it_wrong(
     345                    __FUNCTION__,
     346                    sprintf(
     347                        $message,
     348                        '<code>' . get_post_status( $post ) . '</code>',
     349                        '<code>' . $cap . '</code>'
     350                    ),
     351                    '5.4.0'
     352                );
     353
    253354                $caps[] = 'edit_others_posts';
    254355                break;
     
    269370            break;
    270371        case 'publish_post':
     372
     373
     374
     375
     376
     377
     378
     379
     380
     381
     382
     383
     384
     385
    271386            $post = get_post( $args[0] );
    272387            if ( ! $post ) {
     
    278393            if ( ! $post_type ) {
    279394                /* translators: 1: Post type, 2: Capability name. */
    280                 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
     395                $message = __( 'The post type %1$s is not registered, so it may not be reliable to check the capability %2$s against a post of that type.' );
     396
     397                _doing_it_wrong(
     398                    __FUNCTION__,
     399                    sprintf(
     400                        $message,
     401                        '<code>' . $post->post_type . '</code>',
     402                        '<code>' . $cap . '</code>'
     403                    ),
     404                    '4.4.0'
     405                );
     406
    281407                $caps[] = 'edit_others_posts';
    282408                break;
     
    298424        case 'add_user_meta':
    299425            $object_type = explode( '_', $cap )[1];
    300             $object_id   = (int) $args[0];
     426
     427            if ( ! isset( $args[0] ) ) {
     428                if ( 'post' === $object_type ) {
     429                    /* translators: %s: Capability name. */
     430                    $message = __( 'When checking for the %s capability, you must always check it against a specific post.' );
     431                } elseif ( 'comment' === $object_type ) {
     432                    /* translators: %s: Capability name. */
     433                    $message = __( 'When checking for the %s capability, you must always check it against a specific comment.' );
     434                } elseif ( 'term' === $object_type ) {
     435                    /* translators: %s: Capability name. */
     436                    $message = __( 'When checking for the %s capability, you must always check it against a specific term.' );
     437                } else {
     438                    /* translators: %s: Capability name. */
     439                    $message = __( 'When checking for the %s capability, you must always check it against a specific user.' );
     440                }
     441
     442                _doing_it_wrong(
     443                    __FUNCTION__,
     444                    sprintf( $message, '<code>' . $cap . '</code>' ),
     445                    '6.1.0'
     446                );
     447
     448                $caps[] = 'do_not_allow';
     449                break;
     450            }
     451
     452            $object_id = (int) $args[0];
    301453
    302454            $object_subtype = get_object_subtype( $object_type, $object_id );
     
    393545            break;
    394546        case 'edit_comment':
     547
     548
     549
     550
     551
     552
     553
     554
     555
     556
     557
     558
     559
     560
    395561            $comment = get_comment( $args[0] );
    396562            if ( ! $comment ) {
     
    533699        case 'delete_term':
    534700        case 'assign_term':
     701
     702
     703
     704
     705
     706
     707
     708
     709
     710
     711
     712
     713
     714
    535715            $term_id = (int) $args[0];
    536716            $term    = get_term( $term_id );
  • trunk/tests/phpunit/tests/user.php

    r52650 r53408  
    19751975        );
    19761976
    1977         // _doing_wrong() should be called because the filter callback
     1977        // _doing_wrong() should be called because the filter callback
    19781978        // adds a item with a 'name' that is the same as one generated by core.
    19791979        $this->setExpectedIncorrectUsage( 'wp_user_personal_data_exporter' );
  • trunk/tests/phpunit/tests/user/capabilities.php

    r52824 r53408  
    15961596        $editor = self::$users['editor'];
    15971597
     1598
    15981599        foreach ( $caps as $cap ) {
    15991600            // `null` represents a non-existent term ID.
  • trunk/tests/phpunit/tests/user/mapMetaCap.php

    r52010 r53408  
    44 * @group user
    55 * @group capabilities
     6
    67 */
    78class Tests_User_MapMetaCap extends WP_UnitTestCase {
     
    411412        $this->assertSame( array( 'manage_options' ), $caps );
    412413    }
     414
     415
     416
     417
     418
     419
     420
     421
     422
     423
     424
     425
     426
     427
     428
     429
     430
     431
     432
     433
     434
     435
     436
     437
     438
     439
     440
     441
     442
     443
     444
     445
     446
     447
     448
     449
     450
     451
     452
     453
     454
     455
     456
     457
     458
     459
     460
    413461}
Note: See TracChangeset for help on using the changeset viewer.