From ce34d0ab007bd9a952f5adbaa511f78b9359cde4 Mon Sep 17 00:00:00 2001 From: TimothyBlynJacobs Date: Thu, 22 Oct 2020 15:06:09 +0000 Subject: [PATCH] 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. Built from https://develop.svn.wordpress.org/trunk@49276 git-svn-id: http://core.svn.wordpress.org/trunk@49038 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/authorize-application.php | 13 +++++++++++-- wp-admin/includes/user.php | 8 ++++++++ wp-admin/js/auth-app.js | 7 ++++++- wp-admin/js/auth-app.min.js | 2 +- wp-includes/class-wp-application-passwords.php | 1 + ...ass-wp-rest-application-passwords-controller.php | 11 +++++++++++ wp-includes/version.php | 2 +- 7 files changed, 39 insertions(+), 5 deletions(-) diff --git a/wp-admin/authorize-application.php b/wp-admin/authorize-application.php index 88804b7d74..b9cc0c0aac 100644 --- a/wp-admin/authorize-application.php +++ b/wp-admin/authorize-application.php @@ -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';
+ diff --git a/wp-admin/includes/user.php b/wp-admin/includes/user.php index d519ee75bc..a08fe3f4b2 100644 --- a/wp-admin/includes/user.php +++ b/wp-admin/includes/user.php @@ -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. * diff --git a/wp-admin/js/auth-app.js b/wp-admin/js/auth-app.js index 808bb0754b..3152ed3c5c 100644 --- a/wp-admin/js/auth-app.js +++ b/wp-admin/js/auth-app.js @@ -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. * diff --git a/wp-admin/js/auth-app.min.js b/wp-admin/js/auth-app.min.js index 9ad5a24ee6..56dcf73340 100644 --- a/wp-admin/js/auth-app.min.js +++ b/wp-admin/js/auth-app.min.js @@ -1,2 +1,2 @@ /*! This file is auto-generated */ -!function(i,c){var r=i("#app_name"),d=i("#approve"),e=i("#reject"),l=r.closest("form"),o={userLogin:c.user_login,successUrl:c.success,rejectUrl:c.reject};d.click(function(e){var n=r.val();if(e.preventDefault(),0!==n.length){r.prop("disabled",!0),d.prop("disabled",!0);var s={name:n};s=wp.hooks.applyFilters("wp_application_passwords_approve_app_request",s,o),wp.apiRequest({path:"/wp/v2/users/me/application-passwords",method:"POST",data:s}).done(function(e,s,o){wp.hooks.doAction("wp_application_passwords_approve_app_request_success",e,s,o);var p,a,t,r=c.success;r?(p=r+(-1===r.indexOf("?")?"?":"&")+"user_login="+encodeURIComponent(c.user_login)+"&password="+encodeURIComponent(e.password),window.location=p):(a=wp.i18n.sprintf(wp.i18n.__("Your new password for %1$s is: %2$s."),"",""),t=i("
").attr("role","alert").attr("tabindex",0).addClass("notice notice-success notice-alt").append(i("

").html(a)),i("strong",t).text(n),i("kbd",t).text(e.password),l.replaceWith(t),t.focus())}).fail(function(e,s,o){var p=o,a=null;e.responseJSON&&(a=e.responseJSON).message&&(p=a.message);var t=i("
").attr("role","alert").addClass("notice notice-error").append(i("

").text(p));i("h1").after(t),r.prop("disabled",!1),d.prop("disabled",!1),wp.hooks.doAction("wp_application_passwords_approve_app_request_success",a,s,e)})}else r.focus()}),e.click(function(e){e.preventDefault(),wp.hooks.doAction("wp_application_passwords_reject_app",o),window.location=c.reject}),l.on("submit",function(e){e.preventDefault()})}(jQuery,authApp); \ No newline at end of file +!function(i,c){var n=i("#app_name"),d=i("#approve"),e=i("#reject"),l=n.closest("form"),o={userLogin:c.user_login,successUrl:c.success,rejectUrl:c.reject};d.click(function(e){var r=n.val(),p=i('input[name="app_id"]',l).val();if(e.preventDefault(),0!==r.length){n.prop("disabled",!0),d.prop("disabled",!0);var s={name:r};0",""),t=i("
").attr("role","alert").attr("tabindex",0).addClass("notice notice-success notice-alt").append(i("

").html(a)),i("strong",t).text(r),i("kbd",t).text(e.password),l.replaceWith(t),t.focus())}).fail(function(e,p,s){var o=s,a=null;e.responseJSON&&(a=e.responseJSON).message&&(o=a.message);var t=i("
").attr("role","alert").addClass("notice notice-error").append(i("

").text(o));i("h1").after(t),n.prop("disabled",!1),d.prop("disabled",!1),wp.hooks.doAction("wp_application_passwords_approve_app_request_success",a,p,e)})}else n.focus()}),e.click(function(e){e.preventDefault(),wp.hooks.doAction("wp_application_passwords_reject_app",o),window.location=c.reject}),l.on("submit",function(e){e.preventDefault()})}(jQuery,authApp); \ No newline at end of file diff --git a/wp-includes/class-wp-application-passwords.php b/wp-includes/class-wp-application-passwords.php index 876ffbed18..4500a571d8 100644 --- a/wp-includes/class-wp-application-passwords.php +++ b/wp-includes/class-wp-application-passwords.php @@ -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(), diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php index fdb496b412..123722a793 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php @@ -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', diff --git a/wp-includes/version.php b/wp-includes/version.php index d3619d24d4..ac73ae33b0 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -13,7 +13,7 @@ * * @global string $wp_version */ -$wp_version = '5.6-beta1-49274'; +$wp_version = '5.6-beta1-49276'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.