Make WordPress Core

Changeset 49276


Ignore:
Timestamp:
10/22/2020 03:04:23 PM (3 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.