wp/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php
changeset 18 be944660c56a
child 19 3d72ae0968f4
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
       
     1 <?php
       
     2 /**
       
     3  * REST API: WP_REST_Application_Passwords_Controller class
       
     4  *
       
     5  * @package    WordPress
       
     6  * @subpackage REST_API
       
     7  * @since      5.6.0
       
     8  */
       
     9 
       
    10 /**
       
    11  * Core class to access a user's application passwords via the REST API.
       
    12  *
       
    13  * @since 5.6.0
       
    14  *
       
    15  * @see   WP_REST_Controller
       
    16  */
       
    17 class WP_REST_Application_Passwords_Controller extends WP_REST_Controller {
       
    18 
       
    19 	/**
       
    20 	 * Application Passwords controller constructor.
       
    21 	 *
       
    22 	 * @since 5.6.0
       
    23 	 */
       
    24 	public function __construct() {
       
    25 		$this->namespace = 'wp/v2';
       
    26 		$this->rest_base = 'users/(?P<user_id>(?:[\d]+|me))/application-passwords';
       
    27 	}
       
    28 
       
    29 	/**
       
    30 	 * Registers the REST API routes for the application passwords controller.
       
    31 	 *
       
    32 	 * @since 5.6.0
       
    33 	 */
       
    34 	public function register_routes() {
       
    35 		register_rest_route(
       
    36 			$this->namespace,
       
    37 			'/' . $this->rest_base,
       
    38 			array(
       
    39 				array(
       
    40 					'methods'             => WP_REST_Server::READABLE,
       
    41 					'callback'            => array( $this, 'get_items' ),
       
    42 					'permission_callback' => array( $this, 'get_items_permissions_check' ),
       
    43 					'args'                => $this->get_collection_params(),
       
    44 				),
       
    45 				array(
       
    46 					'methods'             => WP_REST_Server::CREATABLE,
       
    47 					'callback'            => array( $this, 'create_item' ),
       
    48 					'permission_callback' => array( $this, 'create_item_permissions_check' ),
       
    49 					'args'                => $this->get_endpoint_args_for_item_schema(),
       
    50 				),
       
    51 				array(
       
    52 					'methods'             => WP_REST_Server::DELETABLE,
       
    53 					'callback'            => array( $this, 'delete_items' ),
       
    54 					'permission_callback' => array( $this, 'delete_items_permissions_check' ),
       
    55 				),
       
    56 				'schema' => array( $this, 'get_public_item_schema' ),
       
    57 			)
       
    58 		);
       
    59 
       
    60 		register_rest_route(
       
    61 			$this->namespace,
       
    62 			'/' . $this->rest_base . '/introspect',
       
    63 			array(
       
    64 				array(
       
    65 					'methods'             => WP_REST_Server::READABLE,
       
    66 					'callback'            => array( $this, 'get_current_item' ),
       
    67 					'permission_callback' => array( $this, 'get_current_item_permissions_check' ),
       
    68 					'args'                => array(
       
    69 						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
       
    70 					),
       
    71 				),
       
    72 				'schema' => array( $this, 'get_public_item_schema' ),
       
    73 			)
       
    74 		);
       
    75 
       
    76 		register_rest_route(
       
    77 			$this->namespace,
       
    78 			'/' . $this->rest_base . '/(?P<uuid>[\w\-]+)',
       
    79 			array(
       
    80 				array(
       
    81 					'methods'             => WP_REST_Server::READABLE,
       
    82 					'callback'            => array( $this, 'get_item' ),
       
    83 					'permission_callback' => array( $this, 'get_item_permissions_check' ),
       
    84 					'args'                => array(
       
    85 						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
       
    86 					),
       
    87 				),
       
    88 				array(
       
    89 					'methods'             => WP_REST_Server::EDITABLE,
       
    90 					'callback'            => array( $this, 'update_item' ),
       
    91 					'permission_callback' => array( $this, 'update_item_permissions_check' ),
       
    92 					'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
       
    93 				),
       
    94 				array(
       
    95 					'methods'             => WP_REST_Server::DELETABLE,
       
    96 					'callback'            => array( $this, 'delete_item' ),
       
    97 					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
       
    98 				),
       
    99 				'schema' => array( $this, 'get_public_item_schema' ),
       
   100 			)
       
   101 		);
       
   102 	}
       
   103 
       
   104 	/**
       
   105 	 * Checks if a given request has access to get application passwords.
       
   106 	 *
       
   107 	 * @since 5.6.0
       
   108 	 *
       
   109 	 * @param WP_REST_Request $request Full details about the request.
       
   110 	 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
       
   111 	 */
       
   112 	public function get_items_permissions_check( $request ) {
       
   113 		$user = $this->get_user( $request );
       
   114 
       
   115 		if ( is_wp_error( $user ) ) {
       
   116 			return $user;
       
   117 		}
       
   118 
       
   119 		if ( ! current_user_can( 'list_app_passwords', $user->ID ) ) {
       
   120 			return new WP_Error(
       
   121 				'rest_cannot_list_application_passwords',
       
   122 				__( 'Sorry, you are not allowed to list application passwords for this user.' ),
       
   123 				array( 'status' => rest_authorization_required_code() )
       
   124 			);
       
   125 		}
       
   126 
       
   127 		return true;
       
   128 	}
       
   129 
       
   130 	/**
       
   131 	 * Retrieves a collection of application passwords.
       
   132 	 *
       
   133 	 * @since 5.6.0
       
   134 	 *
       
   135 	 * @param WP_REST_Request $request Full details about the request.
       
   136 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   137 	 */
       
   138 	public function get_items( $request ) {
       
   139 		$user = $this->get_user( $request );
       
   140 
       
   141 		if ( is_wp_error( $user ) ) {
       
   142 			return $user;
       
   143 		}
       
   144 
       
   145 		$passwords = WP_Application_Passwords::get_user_application_passwords( $user->ID );
       
   146 		$response  = array();
       
   147 
       
   148 		foreach ( $passwords as $password ) {
       
   149 			$response[] = $this->prepare_response_for_collection(
       
   150 				$this->prepare_item_for_response( $password, $request )
       
   151 			);
       
   152 		}
       
   153 
       
   154 		return new WP_REST_Response( $response );
       
   155 	}
       
   156 
       
   157 	/**
       
   158 	 * Checks if a given request has access to get a specific application password.
       
   159 	 *
       
   160 	 * @since 5.6.0
       
   161 	 *
       
   162 	 * @param WP_REST_Request $request Full details about the request.
       
   163 	 * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
       
   164 	 */
       
   165 	public function get_item_permissions_check( $request ) {
       
   166 		$user = $this->get_user( $request );
       
   167 
       
   168 		if ( is_wp_error( $user ) ) {
       
   169 			return $user;
       
   170 		}
       
   171 
       
   172 		if ( ! current_user_can( 'read_app_password', $user->ID, $request['uuid'] ) ) {
       
   173 			return new WP_Error(
       
   174 				'rest_cannot_read_application_password',
       
   175 				__( 'Sorry, you are not allowed to read this application password.' ),
       
   176 				array( 'status' => rest_authorization_required_code() )
       
   177 			);
       
   178 		}
       
   179 
       
   180 		return true;
       
   181 	}
       
   182 
       
   183 	/**
       
   184 	 * Retrieves one application password from the collection.
       
   185 	 *
       
   186 	 * @since 5.6.0
       
   187 	 *
       
   188 	 * @param WP_REST_Request $request Full details about the request.
       
   189 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   190 	 */
       
   191 	public function get_item( $request ) {
       
   192 		$password = $this->get_application_password( $request );
       
   193 
       
   194 		if ( is_wp_error( $password ) ) {
       
   195 			return $password;
       
   196 		}
       
   197 
       
   198 		return $this->prepare_item_for_response( $password, $request );
       
   199 	}
       
   200 
       
   201 	/**
       
   202 	 * Checks if a given request has access to create application passwords.
       
   203 	 *
       
   204 	 * @since 5.6.0
       
   205 	 *
       
   206 	 * @param WP_REST_Request $request Full details about the request.
       
   207 	 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
       
   208 	 */
       
   209 	public function create_item_permissions_check( $request ) {
       
   210 		$user = $this->get_user( $request );
       
   211 
       
   212 		if ( is_wp_error( $user ) ) {
       
   213 			return $user;
       
   214 		}
       
   215 
       
   216 		if ( ! current_user_can( 'create_app_password', $user->ID ) ) {
       
   217 			return new WP_Error(
       
   218 				'rest_cannot_create_application_passwords',
       
   219 				__( 'Sorry, you are not allowed to create application passwords for this user.' ),
       
   220 				array( 'status' => rest_authorization_required_code() )
       
   221 			);
       
   222 		}
       
   223 
       
   224 		return true;
       
   225 	}
       
   226 
       
   227 	/**
       
   228 	 * Creates an application password.
       
   229 	 *
       
   230 	 * @since 5.6.0
       
   231 	 *
       
   232 	 * @param WP_REST_Request $request Full details about the request.
       
   233 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   234 	 */
       
   235 	public function create_item( $request ) {
       
   236 		$user = $this->get_user( $request );
       
   237 
       
   238 		if ( is_wp_error( $user ) ) {
       
   239 			return $user;
       
   240 		}
       
   241 
       
   242 		$prepared = $this->prepare_item_for_database( $request );
       
   243 
       
   244 		if ( is_wp_error( $prepared ) ) {
       
   245 			return $prepared;
       
   246 		}
       
   247 
       
   248 		$created = WP_Application_Passwords::create_new_application_password( $user->ID, wp_slash( (array) $prepared ) );
       
   249 
       
   250 		if ( is_wp_error( $created ) ) {
       
   251 			return $created;
       
   252 		}
       
   253 
       
   254 		$password = $created[0];
       
   255 		$item     = WP_Application_Passwords::get_user_application_password( $user->ID, $created[1]['uuid'] );
       
   256 
       
   257 		$item['new_password'] = WP_Application_Passwords::chunk_password( $password );
       
   258 		$fields_update        = $this->update_additional_fields_for_object( $item, $request );
       
   259 
       
   260 		if ( is_wp_error( $fields_update ) ) {
       
   261 			return $fields_update;
       
   262 		}
       
   263 
       
   264 		/**
       
   265 		 * Fires after a single application password is completely created or updated via the REST API.
       
   266 		 *
       
   267 		 * @since 5.6.0
       
   268 		 *
       
   269 		 * @param array           $item     Inserted or updated password item.
       
   270 		 * @param WP_REST_Request $request  Request object.
       
   271 		 * @param bool            $creating True when creating an application password, false when updating.
       
   272 		 */
       
   273 		do_action( 'rest_after_insert_application_password', $item, $request, true );
       
   274 
       
   275 		$request->set_param( 'context', 'edit' );
       
   276 		$response = $this->prepare_item_for_response( $item, $request );
       
   277 
       
   278 		$response->set_status( 201 );
       
   279 		$response->header( 'Location', $response->get_links()['self'][0]['href'] );
       
   280 
       
   281 		return $response;
       
   282 	}
       
   283 
       
   284 	/**
       
   285 	 * Checks if a given request has access to update application passwords.
       
   286 	 *
       
   287 	 * @since 5.6.0
       
   288 	 *
       
   289 	 * @param WP_REST_Request $request Full details about the request.
       
   290 	 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
       
   291 	 */
       
   292 	public function update_item_permissions_check( $request ) {
       
   293 		$user = $this->get_user( $request );
       
   294 
       
   295 		if ( is_wp_error( $user ) ) {
       
   296 			return $user;
       
   297 		}
       
   298 
       
   299 		if ( ! current_user_can( 'edit_app_password', $user->ID, $request['uuid'] ) ) {
       
   300 			return new WP_Error(
       
   301 				'rest_cannot_edit_application_password',
       
   302 				__( 'Sorry, you are not allowed to edit this application password.' ),
       
   303 				array( 'status' => rest_authorization_required_code() )
       
   304 			);
       
   305 		}
       
   306 
       
   307 		return true;
       
   308 	}
       
   309 
       
   310 	/**
       
   311 	 * Updates an application password.
       
   312 	 *
       
   313 	 * @since 5.6.0
       
   314 	 *
       
   315 	 * @param WP_REST_Request $request Full details about the request.
       
   316 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   317 	 */
       
   318 	public function update_item( $request ) {
       
   319 		$user = $this->get_user( $request );
       
   320 
       
   321 		if ( is_wp_error( $user ) ) {
       
   322 			return $user;
       
   323 		}
       
   324 
       
   325 		$item = $this->get_application_password( $request );
       
   326 
       
   327 		if ( is_wp_error( $item ) ) {
       
   328 			return $item;
       
   329 		}
       
   330 
       
   331 		$prepared = $this->prepare_item_for_database( $request );
       
   332 
       
   333 		if ( is_wp_error( $prepared ) ) {
       
   334 			return $prepared;
       
   335 		}
       
   336 
       
   337 		$saved = WP_Application_Passwords::update_application_password( $user->ID, $item['uuid'], wp_slash( (array) $prepared ) );
       
   338 
       
   339 		if ( is_wp_error( $saved ) ) {
       
   340 			return $saved;
       
   341 		}
       
   342 
       
   343 		$fields_update = $this->update_additional_fields_for_object( $item, $request );
       
   344 
       
   345 		if ( is_wp_error( $fields_update ) ) {
       
   346 			return $fields_update;
       
   347 		}
       
   348 
       
   349 		$item = WP_Application_Passwords::get_user_application_password( $user->ID, $item['uuid'] );
       
   350 
       
   351 		/** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php */
       
   352 		do_action( 'rest_after_insert_application_password', $item, $request, false );
       
   353 
       
   354 		$request->set_param( 'context', 'edit' );
       
   355 		return $this->prepare_item_for_response( $item, $request );
       
   356 	}
       
   357 
       
   358 	/**
       
   359 	 * Checks if a given request has access to delete all application passwords.
       
   360 	 *
       
   361 	 * @since 5.6.0
       
   362 	 *
       
   363 	 * @param WP_REST_Request $request Full details about the request.
       
   364 	 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
       
   365 	 */
       
   366 	public function delete_items_permissions_check( $request ) {
       
   367 		$user = $this->get_user( $request );
       
   368 
       
   369 		if ( is_wp_error( $user ) ) {
       
   370 			return $user;
       
   371 		}
       
   372 
       
   373 		if ( ! current_user_can( 'delete_app_passwords', $user->ID ) ) {
       
   374 			return new WP_Error(
       
   375 				'rest_cannot_delete_application_passwords',
       
   376 				__( 'Sorry, you are not allowed to delete application passwords for this user.' ),
       
   377 				array( 'status' => rest_authorization_required_code() )
       
   378 			);
       
   379 		}
       
   380 
       
   381 		return true;
       
   382 	}
       
   383 
       
   384 	/**
       
   385 	 * Deletes all application passwords.
       
   386 	 *
       
   387 	 * @since 5.6.0
       
   388 	 *
       
   389 	 * @param WP_REST_Request $request Full details about the request.
       
   390 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   391 	 */
       
   392 	public function delete_items( $request ) {
       
   393 		$user = $this->get_user( $request );
       
   394 
       
   395 		if ( is_wp_error( $user ) ) {
       
   396 			return $user;
       
   397 		}
       
   398 
       
   399 		$deleted = WP_Application_Passwords::delete_all_application_passwords( $user->ID );
       
   400 
       
   401 		if ( is_wp_error( $deleted ) ) {
       
   402 			return $deleted;
       
   403 		}
       
   404 
       
   405 		return new WP_REST_Response(
       
   406 			array(
       
   407 				'deleted' => true,
       
   408 				'count'   => $deleted,
       
   409 			)
       
   410 		);
       
   411 	}
       
   412 
       
   413 	/**
       
   414 	 * Checks if a given request has access to delete a specific application password.
       
   415 	 *
       
   416 	 * @since 5.6.0
       
   417 	 *
       
   418 	 * @param WP_REST_Request $request Full details about the request.
       
   419 	 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
       
   420 	 */
       
   421 	public function delete_item_permissions_check( $request ) {
       
   422 		$user = $this->get_user( $request );
       
   423 
       
   424 		if ( is_wp_error( $user ) ) {
       
   425 			return $user;
       
   426 		}
       
   427 
       
   428 		if ( ! current_user_can( 'delete_app_password', $user->ID, $request['uuid'] ) ) {
       
   429 			return new WP_Error(
       
   430 				'rest_cannot_delete_application_password',
       
   431 				__( 'Sorry, you are not allowed to delete this application password.' ),
       
   432 				array( 'status' => rest_authorization_required_code() )
       
   433 			);
       
   434 		}
       
   435 
       
   436 		return true;
       
   437 	}
       
   438 
       
   439 	/**
       
   440 	 * Deletes one application password.
       
   441 	 *
       
   442 	 * @since 5.6.0
       
   443 	 *
       
   444 	 * @param WP_REST_Request $request Full details about the request.
       
   445 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   446 	 */
       
   447 	public function delete_item( $request ) {
       
   448 		$user = $this->get_user( $request );
       
   449 
       
   450 		if ( is_wp_error( $user ) ) {
       
   451 			return $user;
       
   452 		}
       
   453 
       
   454 		$password = $this->get_application_password( $request );
       
   455 
       
   456 		if ( is_wp_error( $password ) ) {
       
   457 			return $password;
       
   458 		}
       
   459 
       
   460 		$request->set_param( 'context', 'edit' );
       
   461 		$previous = $this->prepare_item_for_response( $password, $request );
       
   462 		$deleted  = WP_Application_Passwords::delete_application_password( $user->ID, $password['uuid'] );
       
   463 
       
   464 		if ( is_wp_error( $deleted ) ) {
       
   465 			return $deleted;
       
   466 		}
       
   467 
       
   468 		return new WP_REST_Response(
       
   469 			array(
       
   470 				'deleted'  => true,
       
   471 				'previous' => $previous->get_data(),
       
   472 			)
       
   473 		);
       
   474 	}
       
   475 
       
   476 	/**
       
   477 	 * Checks if a given request has access to get the currently used application password.
       
   478 	 *
       
   479 	 * @since 5.7.0
       
   480 	 *
       
   481 	 * @param WP_REST_Request $request Full details about the request.
       
   482 	 * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
       
   483 	 */
       
   484 	public function get_current_item_permissions_check( $request ) {
       
   485 		$user = $this->get_user( $request );
       
   486 
       
   487 		if ( is_wp_error( $user ) ) {
       
   488 			return $user;
       
   489 		}
       
   490 
       
   491 		if ( get_current_user_id() !== $user->ID ) {
       
   492 			return new WP_Error(
       
   493 				'rest_cannot_introspect_app_password_for_non_authenticated_user',
       
   494 				__( 'The authenticated Application Password can only be introspected for the current user.' ),
       
   495 				array( 'status' => rest_authorization_required_code() )
       
   496 			);
       
   497 		}
       
   498 
       
   499 		return true;
       
   500 	}
       
   501 
       
   502 	/**
       
   503 	 * Retrieves the application password being currently used for authentication.
       
   504 	 *
       
   505 	 * @since 5.7.0
       
   506 	 *
       
   507 	 * @param WP_REST_Request $request Full details about the request.
       
   508 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   509 	 */
       
   510 	public function get_current_item( $request ) {
       
   511 		$user = $this->get_user( $request );
       
   512 
       
   513 		if ( is_wp_error( $user ) ) {
       
   514 			return $user;
       
   515 		}
       
   516 
       
   517 		$uuid = rest_get_authenticated_app_password();
       
   518 
       
   519 		if ( ! $uuid ) {
       
   520 			return new WP_Error(
       
   521 				'rest_no_authenticated_app_password',
       
   522 				__( 'Cannot introspect Application Password.' ),
       
   523 				array( 'status' => 404 )
       
   524 			);
       
   525 		}
       
   526 
       
   527 		$password = WP_Application_Passwords::get_user_application_password( $user->ID, $uuid );
       
   528 
       
   529 		if ( ! $password ) {
       
   530 			return new WP_Error(
       
   531 				'rest_application_password_not_found',
       
   532 				__( 'Application password not found.' ),
       
   533 				array( 'status' => 500 )
       
   534 			);
       
   535 		}
       
   536 
       
   537 		return $this->prepare_item_for_response( $password, $request );
       
   538 	}
       
   539 
       
   540 	/**
       
   541 	 * Performs a permissions check for the request.
       
   542 	 *
       
   543 	 * @since 5.6.0
       
   544 	 * @deprecated 5.7.0 Use `edit_user` directly or one of the specific meta capabilities introduced in 5.7.0.
       
   545 	 *
       
   546 	 * @param WP_REST_Request $request
       
   547 	 * @return true|WP_Error
       
   548 	 */
       
   549 	protected function do_permissions_check( $request ) {
       
   550 		_deprecated_function( __METHOD__, '5.7.0' );
       
   551 
       
   552 		$user = $this->get_user( $request );
       
   553 
       
   554 		if ( is_wp_error( $user ) ) {
       
   555 			return $user;
       
   556 		}
       
   557 
       
   558 		if ( ! current_user_can( 'edit_user', $user->ID ) ) {
       
   559 			return new WP_Error(
       
   560 				'rest_cannot_manage_application_passwords',
       
   561 				__( 'Sorry, you are not allowed to manage application passwords for this user.' ),
       
   562 				array( 'status' => rest_authorization_required_code() )
       
   563 			);
       
   564 		}
       
   565 
       
   566 		return true;
       
   567 	}
       
   568 
       
   569 	/**
       
   570 	 * Prepares an application password for a create or update operation.
       
   571 	 *
       
   572 	 * @since 5.6.0
       
   573 	 *
       
   574 	 * @param WP_REST_Request $request Request object.
       
   575 	 * @return object|WP_Error The prepared item, or WP_Error object on failure.
       
   576 	 */
       
   577 	protected function prepare_item_for_database( $request ) {
       
   578 		$prepared = (object) array(
       
   579 			'name' => $request['name'],
       
   580 		);
       
   581 
       
   582 		if ( $request['app_id'] && ! $request['uuid'] ) {
       
   583 			$prepared->app_id = $request['app_id'];
       
   584 		}
       
   585 
       
   586 		/**
       
   587 		 * Filters an application password before it is inserted via the REST API.
       
   588 		 *
       
   589 		 * @since 5.6.0
       
   590 		 *
       
   591 		 * @param stdClass        $prepared An object representing a single application password prepared for inserting or updating the database.
       
   592 		 * @param WP_REST_Request $request  Request object.
       
   593 		 */
       
   594 		return apply_filters( 'rest_pre_insert_application_password', $prepared, $request );
       
   595 	}
       
   596 
       
   597 	/**
       
   598 	 * Prepares the application password for the REST response.
       
   599 	 *
       
   600 	 * @since 5.6.0
       
   601 	 *
       
   602 	 * @param array           $item    WordPress representation of the item.
       
   603 	 * @param WP_REST_Request $request Request object.
       
   604 	 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
       
   605 	 */
       
   606 	public function prepare_item_for_response( $item, $request ) {
       
   607 		$user = $this->get_user( $request );
       
   608 
       
   609 		if ( is_wp_error( $user ) ) {
       
   610 			return $user;
       
   611 		}
       
   612 
       
   613 		$prepared = array(
       
   614 			'uuid'      => $item['uuid'],
       
   615 			'app_id'    => empty( $item['app_id'] ) ? '' : $item['app_id'],
       
   616 			'name'      => $item['name'],
       
   617 			'created'   => gmdate( 'Y-m-d\TH:i:s', $item['created'] ),
       
   618 			'last_used' => $item['last_used'] ? gmdate( 'Y-m-d\TH:i:s', $item['last_used'] ) : null,
       
   619 			'last_ip'   => $item['last_ip'] ? $item['last_ip'] : null,
       
   620 		);
       
   621 
       
   622 		if ( isset( $item['new_password'] ) ) {
       
   623 			$prepared['password'] = $item['new_password'];
       
   624 		}
       
   625 
       
   626 		$prepared = $this->add_additional_fields_to_object( $prepared, $request );
       
   627 		$prepared = $this->filter_response_by_context( $prepared, $request['context'] );
       
   628 
       
   629 		$response = new WP_REST_Response( $prepared );
       
   630 		$response->add_links( $this->prepare_links( $user, $item ) );
       
   631 
       
   632 		/**
       
   633 		 * Filters the REST API response for an application password.
       
   634 		 *
       
   635 		 * @since 5.6.0
       
   636 		 *
       
   637 		 * @param WP_REST_Response $response The response object.
       
   638 		 * @param array            $item     The application password array.
       
   639 		 * @param WP_REST_Request  $request  The request object.
       
   640 		 */
       
   641 		return apply_filters( 'rest_prepare_application_password', $response, $item, $request );
       
   642 	}
       
   643 
       
   644 	/**
       
   645 	 * Prepares links for the request.
       
   646 	 *
       
   647 	 * @since 5.6.0
       
   648 	 *
       
   649 	 * @param WP_User $user The requested user.
       
   650 	 * @param array   $item The application password.
       
   651 	 * @return array The list of links.
       
   652 	 */
       
   653 	protected function prepare_links( WP_User $user, $item ) {
       
   654 		return array(
       
   655 			'self' => array(
       
   656 				'href' => rest_url( sprintf( '%s/users/%d/application-passwords/%s', $this->namespace, $user->ID, $item['uuid'] ) ),
       
   657 			),
       
   658 		);
       
   659 	}
       
   660 
       
   661 	/**
       
   662 	 * Gets the requested user.
       
   663 	 *
       
   664 	 * @since 5.6.0
       
   665 	 *
       
   666 	 * @param WP_REST_Request $request The request object.
       
   667 	 * @return WP_User|WP_Error The WordPress user associated with the request, or a WP_Error if none found.
       
   668 	 */
       
   669 	protected function get_user( $request ) {
       
   670 		if ( ! wp_is_application_passwords_available() ) {
       
   671 			return new WP_Error(
       
   672 				'application_passwords_disabled',
       
   673 				__( 'Application passwords are not available.' ),
       
   674 				array( 'status' => 501 )
       
   675 			);
       
   676 		}
       
   677 
       
   678 		$error = new WP_Error(
       
   679 			'rest_user_invalid_id',
       
   680 			__( 'Invalid user ID.' ),
       
   681 			array( 'status' => 404 )
       
   682 		);
       
   683 
       
   684 		$id = $request['user_id'];
       
   685 
       
   686 		if ( 'me' === $id ) {
       
   687 			if ( ! is_user_logged_in() ) {
       
   688 				return new WP_Error(
       
   689 					'rest_not_logged_in',
       
   690 					__( 'You are not currently logged in.' ),
       
   691 					array( 'status' => 401 )
       
   692 				);
       
   693 			}
       
   694 
       
   695 			$user = wp_get_current_user();
       
   696 		} else {
       
   697 			$id = (int) $id;
       
   698 
       
   699 			if ( $id <= 0 ) {
       
   700 				return $error;
       
   701 			}
       
   702 
       
   703 			$user = get_userdata( $id );
       
   704 		}
       
   705 
       
   706 		if ( empty( $user ) || ! $user->exists() ) {
       
   707 			return $error;
       
   708 		}
       
   709 
       
   710 		if ( is_multisite() && ! is_user_member_of_blog( $user->ID ) ) {
       
   711 			return $error;
       
   712 		}
       
   713 
       
   714 		if ( ! wp_is_application_passwords_available_for_user( $user ) ) {
       
   715 			return new WP_Error(
       
   716 				'application_passwords_disabled_for_user',
       
   717 				__( 'Application passwords are not available for your account. Please contact the site administrator for assistance.' ),
       
   718 				array( 'status' => 501 )
       
   719 			);
       
   720 		}
       
   721 
       
   722 		return $user;
       
   723 	}
       
   724 
       
   725 	/**
       
   726 	 * Gets the requested application password.
       
   727 	 *
       
   728 	 * @since 5.6.0
       
   729 	 *
       
   730 	 * @param WP_REST_Request $request The request object.
       
   731 	 * @return array|WP_Error The application password details if found, a WP_Error otherwise.
       
   732 	 */
       
   733 	protected function get_application_password( $request ) {
       
   734 		$user = $this->get_user( $request );
       
   735 
       
   736 		if ( is_wp_error( $user ) ) {
       
   737 			return $user;
       
   738 		}
       
   739 
       
   740 		$password = WP_Application_Passwords::get_user_application_password( $user->ID, $request['uuid'] );
       
   741 
       
   742 		if ( ! $password ) {
       
   743 			return new WP_Error(
       
   744 				'rest_application_password_not_found',
       
   745 				__( 'Application password not found.' ),
       
   746 				array( 'status' => 404 )
       
   747 			);
       
   748 		}
       
   749 
       
   750 		return $password;
       
   751 	}
       
   752 
       
   753 	/**
       
   754 	 * Retrieves the query params for the collections.
       
   755 	 *
       
   756 	 * @since 5.6.0
       
   757 	 *
       
   758 	 * @return array Query parameters for the collection.
       
   759 	 */
       
   760 	public function get_collection_params() {
       
   761 		return array(
       
   762 			'context' => $this->get_context_param( array( 'default' => 'view' ) ),
       
   763 		);
       
   764 	}
       
   765 
       
   766 	/**
       
   767 	 * Retrieves the application password's schema, conforming to JSON Schema.
       
   768 	 *
       
   769 	 * @since 5.6.0
       
   770 	 *
       
   771 	 * @return array Item schema data.
       
   772 	 */
       
   773 	public function get_item_schema() {
       
   774 		if ( $this->schema ) {
       
   775 			return $this->add_additional_fields_schema( $this->schema );
       
   776 		}
       
   777 
       
   778 		$this->schema = array(
       
   779 			'$schema'    => 'http://json-schema.org/draft-04/schema#',
       
   780 			'title'      => 'application-password',
       
   781 			'type'       => 'object',
       
   782 			'properties' => array(
       
   783 				'uuid'      => array(
       
   784 					'description' => __( 'The unique identifier for the application password.' ),
       
   785 					'type'        => 'string',
       
   786 					'format'      => 'uuid',
       
   787 					'context'     => array( 'view', 'edit', 'embed' ),
       
   788 					'readonly'    => true,
       
   789 				),
       
   790 				'app_id'    => array(
       
   791 					'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.' ),
       
   792 					'type'        => 'string',
       
   793 					'format'      => 'uuid',
       
   794 					'context'     => array( 'view', 'edit', 'embed' ),
       
   795 				),
       
   796 				'name'      => array(
       
   797 					'description' => __( 'The name of the application password.' ),
       
   798 					'type'        => 'string',
       
   799 					'required'    => true,
       
   800 					'context'     => array( 'view', 'edit', 'embed' ),
       
   801 					'minLength'   => 1,
       
   802 					'pattern'     => '.*\S.*',
       
   803 				),
       
   804 				'password'  => array(
       
   805 					'description' => __( 'The generated password. Only available after adding an application.' ),
       
   806 					'type'        => 'string',
       
   807 					'context'     => array( 'edit' ),
       
   808 					'readonly'    => true,
       
   809 				),
       
   810 				'created'   => array(
       
   811 					'description' => __( 'The GMT date the application password was created.' ),
       
   812 					'type'        => 'string',
       
   813 					'format'      => 'date-time',
       
   814 					'context'     => array( 'view', 'edit' ),
       
   815 					'readonly'    => true,
       
   816 				),
       
   817 				'last_used' => array(
       
   818 					'description' => __( 'The GMT date the application password was last used.' ),
       
   819 					'type'        => array( 'string', 'null' ),
       
   820 					'format'      => 'date-time',
       
   821 					'context'     => array( 'view', 'edit' ),
       
   822 					'readonly'    => true,
       
   823 				),
       
   824 				'last_ip'   => array(
       
   825 					'description' => __( 'The IP address the application password was last used by.' ),
       
   826 					'type'        => array( 'string', 'null' ),
       
   827 					'format'      => 'ip',
       
   828 					'context'     => array( 'view', 'edit' ),
       
   829 					'readonly'    => true,
       
   830 				),
       
   831 			),
       
   832 		);
       
   833 
       
   834 		return $this->add_additional_fields_schema( $this->schema );
       
   835 	}
       
   836 }