Make WordPress Core

Changeset 49276


Ignore:
Timestamp:
10/22/2020 03:04:23 PM (6 years ago)
Author:
TimothyBlynJacobs
Message:

App Passwords: Support an app_id to uniquely identify instances of an app.

Apps may now optionally include an app_id parameter when directing the user to the Authorize Application screen. This allows for instances of an application to be identified and potentially revoked or blocked.

Props TimothyBlynJacobs, georgestephanis.
Fixes #51583.

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/js/_enqueues/admin/auth-app.js

    r49109 r49276  
    1717
    1818        $approveBtn.click( function( e ) {
    19                 var name = $appNameField.val();
     19                var name = $appNameField.val(),
     20                        appId = $( 'input[name="app_id"]', $form ).val();
    2021
    2122                e.preventDefault();
     
    3233                        name: name
    3334                };
     35
     36                if ( appId.length > 0 ) {
     37                        request.app_id = appId;
     38                }
    3439
    3540                /**
  • trunk/src/wp-admin/authorize-application.php

    r49272 r49276  
    1919        $reject_url  = $_POST['reject_url'];
    2020        $app_name    = $_POST['app_name'];
     21        $app_id      = $_POST['app_id'];
    2122        $redirect    = '';
    2223
     
    2829                }
    2930        } elseif ( isset( $_POST['approve'] ) ) {
    30                 $created = WP_Application_Passwords::create_new_application_password( get_current_user_id(), array( 'name' => $app_name ) );
     31                $created = WP_Application_Passwords::create_new_application_password(
     32                        get_current_user_id(),
     33                        array(
     34                                'name'   => $app_name,
     35                                'app_id' => $app_id,
     36                        )
     37                );
    3138
    3239                if ( is_wp_error( $created ) ) {
     
    5764
    5865$app_name    = ! empty( $_REQUEST['app_name'] ) ? $_REQUEST['app_name'] : '';
     66$app_id      = ! empty( $_REQUEST['app_id'] ) ? $_REQUEST['app_id'] : '';
    5967$success_url = ! empty( $_REQUEST['success_url'] ) ? $_REQUEST['success_url'] : null;
    6068
     
    6977$user = wp_get_current_user();
    7078
    71 $request  = compact( 'app_name', 'success_url', 'reject_url' );
     79$request  = compact( 'app_name', 'app_id', 'success_url', 'reject_url' );
    7280$is_valid = wp_is_authorize_application_password_request_valid( $request, $user );
    7381
     
    184192                                <?php wp_nonce_field( 'authorize_application_password' ); ?>
    185193                                <input type="hidden" name="action" value="authorize_application_password" />
     194                                <input type="hidden" name="app_id" value="<?php echo esc_attr( $app_id ); ?>" />
    186195                                <input type="hidden" name="success_url" value="<?php echo esc_url( $success_url ); ?>" />
    187196                                <input type="hidden" name="reject_url" value="<?php echo esc_url( $reject_url ); ?>" />
  • trunk/src/wp-admin/includes/user.php

    r49197 r49276  
    605605 *
    606606 *     @type string $app_name    The suggested name of the application.
     607 *     @type string $app_id      A uuid provided by the application to uniquely identify it.
    607608 *     @type string $success_url The url the user will be redirected to after approving the application.
    608609 *     @type string $reject_url  The url the user will be redirected to after rejecting the application.
     
    636637        }
    637638
     639        if ( ! empty( $request['app_id'] ) && ! wp_is_uuid( $request['app_id'] ) ) {
     640                $error->add(
     641                        'invalid_app_id',
     642                        __( 'The app id must be a uuid.' )
     643                );
     644        }
     645
    638646        /**
    639647         * Fires before application password errors are returned.
  • trunk/src/wp-includes/class-wp-application-passwords.php

    r49109 r49276  
    5252                $new_item = array(
    5353                        'uuid'      => wp_generate_uuid4(),
     54                        'app_id'    => empty( $args['app_id'] ) ? '' : $args['app_id'],
    5455                        'name'      => $args['name'],
    5556                        'password'  => $hashed_password,
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php

    r49109 r49276  
    413413                );
    414414
     415                if ( $request['app_id'] && ! $request['uuid'] ) {
     416                        $prepared->app_id = $request['app_id'];
     417                }
     418
    415419                /**
    416420                 * Filters an application password before it is inserted via the REST API.
     
    442446                $prepared = array(
    443447                        'uuid'      => $item['uuid'],
     448                        'app_id'    => empty( $item['app_id'] ) ? '' : $item['app_id'],
    444449                        'name'      => $item['name'],
    445450                        'created'   => gmdate( 'Y-m-d\TH:i:s', $item['created'] ),
     
    616621                                        'readonly'    => true,
    617622                                ),
     623                                'app_id'    => array(
     624                                        'description' => __( 'A uuid provided by the application to uniquely identify it. It is recommended to use an UUID v5 with the URL or DNS namespace.' ),
     625                                        'type'        => 'string',
     626                                        'format'      => 'uuid',
     627                                        'context'     => array( 'view', 'edit', 'embed' ),
     628                                ),
    618629                                'name'      => array(
    619630                                        'description' => __( 'The name of the application password.' ),
  • trunk/tests/phpunit/tests/rest-api/rest-application-passwords-controller.php

    r49109 r49276  
    309309                wp_set_current_user( self::$admin );
    310310
     311                $app_id  = wp_generate_uuid4();
    311312                $request = new WP_REST_Request( 'POST', '/wp/v2/users/me/application-passwords' );
    312                 $request->set_body_params( array( 'name' => 'App' ) );
     313                $request->set_body_params(
     314                        array(
     315                                'name'   => 'App',
     316                                'app_id' => $app_id,
     317                        )
     318                );
    313319                $response = rest_do_request( $request );
    314320
     
    319325                $this->check_response( $response->get_data(), $passwords[0], true );
    320326                $this->assertEquals( 'App', $response->get_data()['name'] );
     327                $this->assertEquals( $app_id, $response->get_data()['app_id'] );
    321328                $this->assertNull( $response->get_data()['last_used'] );
    322329                $this->assertNull( $response->get_data()['last_ip'] );
     
    512519                $response = rest_do_request( $request );
    513520                $this->assertErrorResponse( 'rest_application_password_not_found', $response, 404 );
     521        }
     522
     523        /**
     524         * @ticket 51583
     525         */
     526        public function test_update_item_cannot_overwrite_app_id() {
     527                wp_set_current_user( self::$admin );
     528                list( , $item ) = WP_Application_Passwords::create_new_application_password( self::$admin, array( 'name' => 'App' ) );
     529
     530                $uuid    = $item['uuid'];
     531                $request = new WP_REST_Request( 'PUT', '/wp/v2/users/me/application-passwords/' . $uuid );
     532                $request->set_body_params( array( 'app_id' => wp_generate_uuid4() ) );
     533                $response = rest_do_request( $request );
     534                $this->assertEquals( '', $response->get_data()['app_id'] );
     535
     536                $app_id = wp_generate_uuid4();
     537
     538                list( , $item ) = WP_Application_Passwords::create_new_application_password(
     539                        self::$admin,
     540                        array(
     541                                'name'   => 'App',
     542                                'app_id' => $app_id,
     543                        )
     544                );
     545
     546                $uuid    = $item['uuid'];
     547                $request = new WP_REST_Request( 'PUT', '/wp/v2/users/me/application-passwords/' . $uuid );
     548                $request->set_body_params( array( 'app_id' => wp_generate_uuid4() ) );
     549                $response = rest_do_request( $request );
     550                $this->assertEquals( $app_id, $response->get_data()['app_id'] );
    514551        }
    515552
     
    776813        protected function check_response( $response, $item, $password = false ) {
    777814                $this->assertArrayHasKey( 'uuid', $response );
     815                $this->assertArrayHasKey( 'app_id', $response );
    778816                $this->assertArrayHasKey( 'name', $response );
    779817                $this->assertArrayHasKey( 'created', $response );
     
    782820
    783821                $this->assertEquals( $item['uuid'], $response['uuid'] );
     822                $this->assertEquals( $item['app_id'], $response['app_id'] );
    784823                $this->assertEquals( $item['name'], $response['name'] );
    785824                $this->assertEquals( gmdate( 'Y-m-d\TH:i:s', $item['created'] ), $response['created'] );
     
    813852                $properties = $data['schema']['properties'];
    814853
    815                 $this->assertCount( 6, $properties );
    816854                $this->assertArrayHasKey( 'uuid', $properties );
     855                $this->assertArrayHasKey( 'app_id', $properties );
    817856                $this->assertArrayHasKey( 'name', $properties );
    818857                $this->assertArrayHasKey( 'password', $properties );
     
    820859                $this->assertArrayHasKey( 'last_used', $properties );
    821860                $this->assertArrayHasKey( 'last_ip', $properties );
     861                $this->assertCount( 7, $properties );
    822862        }
    823863}
  • trunk/tests/qunit/fixtures/wp-api-generated.js

    r49257 r49276  
    49184918                    ],
    49194919                    "args": {
     4920                        "app_id": {
     4921                            "description": "A machine-readable string provided by the application to uniquely identify it.",
     4922                            "type": "string",
     4923                            "required": false
     4924                        },
    49204925                        "name": {
    49214926                            "description": "The name of the application password.",
     
    49684973                    ],
    49694974                    "args": {
     4975                        "app_id": {
     4976                            "description": "A machine-readable string provided by the application to uniquely identify it.",
     4977                            "type": "string",
     4978                            "required": false
     4979                        },
    49704980                        "name": {
    49714981                            "description": "The name of the application password.",
Note: See TracChangeset for help on using the changeset viewer.