Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/wp-includes/class-wp-application-passwords.php
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,47 @@ public static function delete_all_application_passwords( $user_id ) {
return 0;
}

/**
* Deletes all application passwords for the given user with a matching app ID.
*
* @since 7.1.0
*
* @param int $user_id User ID.
* @param string $app_id The application ID.
* @return int|WP_Error The number of passwords that were deleted or a WP_Error on failure.
*/
public static function delete_application_passwords_by_app_id( $user_id, $app_id ) {
$passwords = static::get_user_application_passwords( $user_id );
$remaining = array();
$deleted_passwords = array();

foreach ( $passwords as $password ) {
if ( $password['app_id'] === $app_id ) {
$deleted_passwords[] = $password;
continue;
}

$remaining[] = $password;
}

if ( ! $deleted_passwords ) {
return 0;
}

$saved = static::set_user_application_passwords( $user_id, $remaining );

if ( ! $saved ) {
return new WP_Error( 'db_error', __( 'Could not delete application passwords.' ) );
}

foreach ( $deleted_passwords as $password ) {
/** This action is documented in wp-includes/class-wp-application-passwords.php */
do_action( 'wp_delete_application_password', $user_id, $password );
}

return count( $deleted_passwords );
}

/**
* Sets a user's application passwords.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public function register_routes() {
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_items' ),
'permission_callback' => array( $this, 'delete_items_permissions_check' ),
'args' => $this->get_delete_collection_params(),
),
'schema' => array( $this, 'get_public_item_schema' ),
)
Expand Down Expand Up @@ -385,6 +386,7 @@ public function delete_items_permissions_check( $request ) {
* Deletes all application passwords for a user.
*
* @since 5.6.0
* @since 7.1.0 Supports deleting application passwords by app ID.
*
* @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.
Expand All @@ -396,7 +398,11 @@ public function delete_items( $request ) {
return $user;
}

$deleted = WP_Application_Passwords::delete_all_application_passwords( $user->ID );
if ( ! empty( $request['app_id'] ) ) {
$deleted = WP_Application_Passwords::delete_application_passwords_by_app_id( $user->ID, $request['app_id'] );
} else {
$deleted = WP_Application_Passwords::delete_all_application_passwords( $user->ID );
}

if ( is_wp_error( $deleted ) ) {
return $deleted;
Expand All @@ -410,6 +416,25 @@ public function delete_items( $request ) {
);
}

/**
* Retrieves the query params for deleting a collection of application passwords.
*
* @since 7.1.0
*
* @return array Query parameters for the delete collection endpoint.
*/
protected function get_delete_collection_params() {
return array(
'app_id' => array(
'description' => __( 'The app ID of application passwords to delete.' ),
'type' => 'string',
'format' => 'uuid',
'sanitize_callback' => 'sanitize_text_field',
'validate_callback' => 'rest_validate_request_arg',
),
);
}

/**
* Checks if a given request has access to delete a specific application password for a user.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,97 @@
$this->assertErrorResponse( 'rest_user_invalid_id', $response, 404 );
}

/**
* @ticket 61644
*/
public function test_delete_items_by_app_id() {
wp_set_current_user( self::$admin );

$app_id = wp_generate_uuid4();

list( , $first_item ) = WP_Application_Passwords::create_new_application_password(

Check warning on line 805 in tests/phpunit/tests/rest-api/rest-application-passwords-controller.php

View workflow job for this annotation

GitHub Actions / Coding standards / PHP checks

Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space
self::$admin,
array(
'name' => 'App 1',
'app_id' => $app_id,
)
);
list( , $second_item ) = WP_Application_Passwords::create_new_application_password(
self::$admin,
array(
'name' => 'App 2',
'app_id' => $app_id,
)
);
list( , $third_item ) = WP_Application_Passwords::create_new_application_password(

Check warning on line 819 in tests/phpunit/tests/rest-api/rest-application-passwords-controller.php

View workflow job for this annotation

GitHub Actions / Coding standards / PHP checks

Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space
self::$admin,
array(
'name' => 'App 3',
'app_id' => wp_generate_uuid4(),
)
);

$request = new WP_REST_Request( 'DELETE', '/wp/v2/users/me/application-passwords' );
$request->set_query_params(
array(
'app_id' => $app_id,
)
);
$response = rest_do_request( $request );

$this->assertSame( 200, $response->get_status() );
$this->assertTrue( $response->get_data()['deleted'] );
$this->assertSame( 2, $response->get_data()['count'] );
$this->assertNull( WP_Application_Passwords::get_user_application_password( self::$admin, $first_item['uuid'] ) );
$this->assertNull( WP_Application_Passwords::get_user_application_password( self::$admin, $second_item['uuid'] ) );
$this->assertSame( $third_item, WP_Application_Passwords::get_user_application_password( self::$admin, $third_item['uuid'] ) );
}

/**
* @ticket 61644
*/
public function test_delete_items_by_app_id_with_no_matches() {
wp_set_current_user( self::$admin );

list( , $item ) = WP_Application_Passwords::create_new_application_password(
self::$admin,
array(
'name' => 'App 1',
'app_id' => wp_generate_uuid4(),
)
);

$request = new WP_REST_Request( 'DELETE', '/wp/v2/users/me/application-passwords' );
$request->set_query_params(
array(
'app_id' => wp_generate_uuid4(),
)
);
$response = rest_do_request( $request );

$this->assertSame( 200, $response->get_status() );
$this->assertTrue( $response->get_data()['deleted'] );
$this->assertSame( 0, $response->get_data()['count'] );
$this->assertSame( $item, WP_Application_Passwords::get_user_application_password( self::$admin, $item['uuid'] ) );
}

/**
* @ticket 61644
*/
public function test_delete_items_by_app_id_rejects_invalid_app_id() {
wp_set_current_user( self::$admin );

$request = new WP_REST_Request( 'DELETE', '/wp/v2/users/me/application-passwords' );
$request->set_query_params(
array(
'app_id' => 'not-a-uuid',
)
);
$response = rest_do_request( $request );

$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
}

/**
* @ticket 42790
*/
Expand Down
Loading