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. git-svn-id: https://develop.svn.wordpress.org/trunk@49276 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
e4fadc6f4d
commit
fe2053f2c1
@ -16,7 +16,8 @@
|
||||
};
|
||||
|
||||
$approveBtn.click( function( e ) {
|
||||
var name = $appNameField.val();
|
||||
var name = $appNameField.val(),
|
||||
appId = $( 'input[name="app_id"]', $form ).val();
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
@ -32,6 +33,10 @@
|
||||
name: name
|
||||
};
|
||||
|
||||
if ( appId.length > 0 ) {
|
||||
request.app_id = appId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the request data used to Authorize an Application Password request.
|
||||
*
|
||||
|
@ -18,6 +18,7 @@ if ( isset( $_POST['action'] ) && 'authorize_application_password' === $_POST['a
|
||||
$success_url = $_POST['success_url'];
|
||||
$reject_url = $_POST['reject_url'];
|
||||
$app_name = $_POST['app_name'];
|
||||
$app_id = $_POST['app_id'];
|
||||
$redirect = '';
|
||||
|
||||
if ( isset( $_POST['reject'] ) ) {
|
||||
@ -27,7 +28,13 @@ if ( isset( $_POST['action'] ) && 'authorize_application_password' === $_POST['a
|
||||
$redirect = admin_url();
|
||||
}
|
||||
} elseif ( isset( $_POST['approve'] ) ) {
|
||||
$created = WP_Application_Passwords::create_new_application_password( get_current_user_id(), array( 'name' => $app_name ) );
|
||||
$created = WP_Application_Passwords::create_new_application_password(
|
||||
get_current_user_id(),
|
||||
array(
|
||||
'name' => $app_name,
|
||||
'app_id' => $app_id,
|
||||
)
|
||||
);
|
||||
|
||||
if ( is_wp_error( $created ) ) {
|
||||
$error = $created;
|
||||
@ -56,6 +63,7 @@ if ( isset( $_POST['action'] ) && 'authorize_application_password' === $_POST['a
|
||||
$title = __( 'Authorize Application' );
|
||||
|
||||
$app_name = ! empty( $_REQUEST['app_name'] ) ? $_REQUEST['app_name'] : '';
|
||||
$app_id = ! empty( $_REQUEST['app_id'] ) ? $_REQUEST['app_id'] : '';
|
||||
$success_url = ! empty( $_REQUEST['success_url'] ) ? $_REQUEST['success_url'] : null;
|
||||
|
||||
if ( ! empty( $_REQUEST['reject_url'] ) ) {
|
||||
@ -68,7 +76,7 @@ if ( ! empty( $_REQUEST['reject_url'] ) ) {
|
||||
|
||||
$user = wp_get_current_user();
|
||||
|
||||
$request = compact( 'app_name', 'success_url', 'reject_url' );
|
||||
$request = compact( 'app_name', 'app_id', 'success_url', 'reject_url' );
|
||||
$is_valid = wp_is_authorize_application_password_request_valid( $request, $user );
|
||||
|
||||
if ( is_wp_error( $is_valid ) ) {
|
||||
@ -183,6 +191,7 @@ require_once ABSPATH . 'wp-admin/admin-header.php';
|
||||
<form action="<?php echo esc_url( admin_url( 'authorize-application.php' ) ); ?>" method="post">
|
||||
<?php wp_nonce_field( 'authorize_application_password' ); ?>
|
||||
<input type="hidden" name="action" value="authorize_application_password" />
|
||||
<input type="hidden" name="app_id" value="<?php echo esc_attr( $app_id ); ?>" />
|
||||
<input type="hidden" name="success_url" value="<?php echo esc_url( $success_url ); ?>" />
|
||||
<input type="hidden" name="reject_url" value="<?php echo esc_url( $reject_url ); ?>" />
|
||||
|
||||
|
@ -604,6 +604,7 @@ Please click the following link to activate your user account:
|
||||
* The array of request data. All arguments are optional and may be empty.
|
||||
*
|
||||
* @type string $app_name The suggested name of the application.
|
||||
* @type string $app_id A uuid provided by the application to uniquely identify it.
|
||||
* @type string $success_url The url the user will be redirected to after approving the application.
|
||||
* @type string $reject_url The url the user will be redirected to after rejecting the application.
|
||||
* }
|
||||
@ -635,6 +636,13 @@ function wp_is_authorize_application_password_request_valid( $request, $user ) {
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $request['app_id'] ) && ! wp_is_uuid( $request['app_id'] ) ) {
|
||||
$error->add(
|
||||
'invalid_app_id',
|
||||
__( 'The app id must be a uuid.' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires before application password errors are returned.
|
||||
*
|
||||
|
@ -51,6 +51,7 @@ class WP_Application_Passwords {
|
||||
|
||||
$new_item = array(
|
||||
'uuid' => wp_generate_uuid4(),
|
||||
'app_id' => empty( $args['app_id'] ) ? '' : $args['app_id'],
|
||||
'name' => $args['name'],
|
||||
'password' => $hashed_password,
|
||||
'created' => time(),
|
||||
|
@ -412,6 +412,10 @@ class WP_REST_Application_Passwords_Controller extends WP_REST_Controller {
|
||||
'name' => $request['name'],
|
||||
);
|
||||
|
||||
if ( $request['app_id'] && ! $request['uuid'] ) {
|
||||
$prepared->app_id = $request['app_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters an application password before it is inserted via the REST API.
|
||||
*
|
||||
@ -441,6 +445,7 @@ class WP_REST_Application_Passwords_Controller extends WP_REST_Controller {
|
||||
|
||||
$prepared = array(
|
||||
'uuid' => $item['uuid'],
|
||||
'app_id' => empty( $item['app_id'] ) ? '' : $item['app_id'],
|
||||
'name' => $item['name'],
|
||||
'created' => gmdate( 'Y-m-d\TH:i:s', $item['created'] ),
|
||||
'last_used' => $item['last_used'] ? gmdate( 'Y-m-d\TH:i:s', $item['last_used'] ) : null,
|
||||
@ -615,6 +620,12 @@ class WP_REST_Application_Passwords_Controller extends WP_REST_Controller {
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
'readonly' => true,
|
||||
),
|
||||
'app_id' => array(
|
||||
'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.' ),
|
||||
'type' => 'string',
|
||||
'format' => 'uuid',
|
||||
'context' => array( 'view', 'edit', 'embed' ),
|
||||
),
|
||||
'name' => array(
|
||||
'description' => __( 'The name of the application password.' ),
|
||||
'type' => 'string',
|
||||
|
@ -308,8 +308,14 @@ class WP_Test_REST_Application_Passwords_Controller extends WP_Test_REST_Control
|
||||
public function test_create_item() {
|
||||
wp_set_current_user( self::$admin );
|
||||
|
||||
$app_id = wp_generate_uuid4();
|
||||
$request = new WP_REST_Request( 'POST', '/wp/v2/users/me/application-passwords' );
|
||||
$request->set_body_params( array( 'name' => 'App' ) );
|
||||
$request->set_body_params(
|
||||
array(
|
||||
'name' => 'App',
|
||||
'app_id' => $app_id,
|
||||
)
|
||||
);
|
||||
$response = rest_do_request( $request );
|
||||
|
||||
$this->assertEquals( 201, $response->get_status() );
|
||||
@ -318,6 +324,7 @@ class WP_Test_REST_Application_Passwords_Controller extends WP_Test_REST_Control
|
||||
$this->assertCount( 1, $passwords );
|
||||
$this->check_response( $response->get_data(), $passwords[0], true );
|
||||
$this->assertEquals( 'App', $response->get_data()['name'] );
|
||||
$this->assertEquals( $app_id, $response->get_data()['app_id'] );
|
||||
$this->assertNull( $response->get_data()['last_used'] );
|
||||
$this->assertNull( $response->get_data()['last_ip'] );
|
||||
}
|
||||
@ -513,6 +520,36 @@ class WP_Test_REST_Application_Passwords_Controller extends WP_Test_REST_Control
|
||||
$this->assertErrorResponse( 'rest_application_password_not_found', $response, 404 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 51583
|
||||
*/
|
||||
public function test_update_item_cannot_overwrite_app_id() {
|
||||
wp_set_current_user( self::$admin );
|
||||
list( , $item ) = WP_Application_Passwords::create_new_application_password( self::$admin, array( 'name' => 'App' ) );
|
||||
|
||||
$uuid = $item['uuid'];
|
||||
$request = new WP_REST_Request( 'PUT', '/wp/v2/users/me/application-passwords/' . $uuid );
|
||||
$request->set_body_params( array( 'app_id' => wp_generate_uuid4() ) );
|
||||
$response = rest_do_request( $request );
|
||||
$this->assertEquals( '', $response->get_data()['app_id'] );
|
||||
|
||||
$app_id = wp_generate_uuid4();
|
||||
|
||||
list( , $item ) = WP_Application_Passwords::create_new_application_password(
|
||||
self::$admin,
|
||||
array(
|
||||
'name' => 'App',
|
||||
'app_id' => $app_id,
|
||||
)
|
||||
);
|
||||
|
||||
$uuid = $item['uuid'];
|
||||
$request = new WP_REST_Request( 'PUT', '/wp/v2/users/me/application-passwords/' . $uuid );
|
||||
$request->set_body_params( array( 'app_id' => wp_generate_uuid4() ) );
|
||||
$response = rest_do_request( $request );
|
||||
$this->assertEquals( $app_id, $response->get_data()['app_id'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 42790
|
||||
*/
|
||||
@ -775,12 +812,14 @@ class WP_Test_REST_Application_Passwords_Controller extends WP_Test_REST_Control
|
||||
*/
|
||||
protected function check_response( $response, $item, $password = false ) {
|
||||
$this->assertArrayHasKey( 'uuid', $response );
|
||||
$this->assertArrayHasKey( 'app_id', $response );
|
||||
$this->assertArrayHasKey( 'name', $response );
|
||||
$this->assertArrayHasKey( 'created', $response );
|
||||
$this->assertArrayHasKey( 'last_used', $response );
|
||||
$this->assertArrayHasKey( 'last_ip', $response );
|
||||
|
||||
$this->assertEquals( $item['uuid'], $response['uuid'] );
|
||||
$this->assertEquals( $item['app_id'], $response['app_id'] );
|
||||
$this->assertEquals( $item['name'], $response['name'] );
|
||||
$this->assertEquals( gmdate( 'Y-m-d\TH:i:s', $item['created'] ), $response['created'] );
|
||||
|
||||
@ -812,12 +851,13 @@ class WP_Test_REST_Application_Passwords_Controller extends WP_Test_REST_Control
|
||||
$data = $response->get_data();
|
||||
$properties = $data['schema']['properties'];
|
||||
|
||||
$this->assertCount( 6, $properties );
|
||||
$this->assertArrayHasKey( 'uuid', $properties );
|
||||
$this->assertArrayHasKey( 'app_id', $properties );
|
||||
$this->assertArrayHasKey( 'name', $properties );
|
||||
$this->assertArrayHasKey( 'password', $properties );
|
||||
$this->assertArrayHasKey( 'created', $properties );
|
||||
$this->assertArrayHasKey( 'last_used', $properties );
|
||||
$this->assertArrayHasKey( 'last_ip', $properties );
|
||||
$this->assertCount( 7, $properties );
|
||||
}
|
||||
}
|
||||
|
@ -4917,6 +4917,11 @@ mockedApiResponse.Schema = {
|
||||
"POST"
|
||||
],
|
||||
"args": {
|
||||
"app_id": {
|
||||
"description": "A machine-readable string provided by the application to uniquely identify it.",
|
||||
"type": "string",
|
||||
"required": false
|
||||
},
|
||||
"name": {
|
||||
"description": "The name of the application password.",
|
||||
"type": "string",
|
||||
@ -4967,6 +4972,11 @@ mockedApiResponse.Schema = {
|
||||
"PATCH"
|
||||
],
|
||||
"args": {
|
||||
"app_id": {
|
||||
"description": "A machine-readable string provided by the application to uniquely identify it.",
|
||||
"type": "string",
|
||||
"required": false
|
||||
},
|
||||
"name": {
|
||||
"description": "The name of the application password.",
|
||||
"type": "string",
|
||||
|
Loading…
Reference in New Issue
Block a user