diff --git a/wp-includes/default-filters.php b/wp-includes/default-filters.php index aeb0aec8bc..6babfb4785 100644 --- a/wp-includes/default-filters.php +++ b/wp-includes/default-filters.php @@ -280,7 +280,7 @@ add_action( 'auth_cookie_bad_username', 'rest_cookie_collect_status' ); add_action( 'auth_cookie_bad_hash', 'rest_cookie_collect_status' ); add_action( 'auth_cookie_valid', 'rest_cookie_collect_status' ); add_action( 'application_password_failed_authentication', 'rest_application_password_collect_status' ); -add_action( 'application_password_did_authenticate', 'rest_application_password_collect_status' ); +add_action( 'application_password_did_authenticate', 'rest_application_password_collect_status', 10, 2 ); add_filter( 'rest_authentication_errors', 'rest_application_password_check_errors', 90 ); add_filter( 'rest_authentication_errors', 'rest_cookie_check_errors', 100 ); diff --git a/wp-includes/rest-api.php b/wp-includes/rest-api.php index a2dc9c09a4..536efea696 100644 --- a/wp-includes/rest-api.php +++ b/wp-includes/rest-api.php @@ -1048,15 +1048,39 @@ function rest_cookie_collect_status() { * Collects the status of authenticating with an application password. * * @since 5.6.0 + * @since 5.7.0 Added the `$app_password` parameter. * * @global WP_User|WP_Error|null $wp_rest_application_password_status + * @global string|null $wp_rest_application_password_uuid * * @param WP_Error $user_or_error The authenticated user or error instance. + * @param array $app_password The Application Password used to authenticate. */ -function rest_application_password_collect_status( $user_or_error ) { - global $wp_rest_application_password_status; +function rest_application_password_collect_status( $user_or_error, $app_password = array() ) { + global $wp_rest_application_password_status, $wp_rest_application_password_uuid; $wp_rest_application_password_status = $user_or_error; + + if ( empty( $app_password['uuid'] ) ) { + $wp_rest_application_password_uuid = null; + } else { + $wp_rest_application_password_uuid = $app_password['uuid']; + } +} + +/** + * Gets the Application Password used for authenticating the request. + * + * @since 5.7.0 + * + * @global string|null $wp_rest_application_password_uuid + * + * @return string|null The App Password UUID, or null if Application Passwords was not used. + */ +function rest_get_authenticated_app_password() { + global $wp_rest_application_password_uuid; + + return $wp_rest_application_password_uuid; } /** 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 622d3617c2..c3474df09a 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 @@ -57,6 +57,22 @@ class WP_REST_Application_Passwords_Controller extends WP_REST_Controller { ) ); + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/introspect', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_current_item' ), + 'permission_callback' => array( $this, 'get_current_item_permissions_check' ), + 'args' => array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w\-]+)', @@ -373,6 +389,70 @@ class WP_REST_Application_Passwords_Controller extends WP_REST_Controller { ); } + /** + * Checks if a given request has access to get the currently used application password. + * + * @since 5.7.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise. + */ + public function get_current_item_permissions_check( $request ) { + $user = $this->get_user( $request ); + + if ( is_wp_error( $user ) ) { + return $user; + } + + if ( get_current_user_id() !== $user->ID ) { + return new WP_Error( + 'rest_cannot_introspect_app_password_for_non_authenticated_user', + __( 'The authenticated Application Password can only be introspected for the current user.' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + return true; + } + + /** + * Retrieves the application password being currently used for authentication. + * + * @since 5.7.0 + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. + */ + public function get_current_item( $request ) { + $user = $this->get_user( $request ); + + if ( is_wp_error( $user ) ) { + return $user; + } + + $uuid = rest_get_authenticated_app_password(); + + if ( ! $uuid ) { + return new WP_Error( + 'rest_no_authenticated_app_password', + __( 'Cannot introspect Application Password.' ), + array( 'status' => 404 ) + ); + } + + $password = WP_Application_Passwords::get_user_application_password( $user->ID, $uuid ); + + if ( ! $password ) { + return new WP_Error( + 'rest_application_password_not_found', + __( 'Application password not found.' ), + array( 'status' => 500 ) + ); + } + + return $this->prepare_item_for_response( $password, $request ); + } + /** * Performs a permissions check for the request. * diff --git a/wp-includes/version.php b/wp-includes/version.php index 6c95072ce7..005966860b 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -13,7 +13,7 @@ * * @global string $wp_version */ -$wp_version = '5.7-alpha-50060'; +$wp_version = '5.7-alpha-50065'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.