diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php index 767917d6f6fd0..401bbfd5ceefc 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php @@ -719,7 +719,7 @@ protected function get_user( $request ) { return $error; } - if ( is_multisite() && ! user_can( $user->ID, 'manage_sites' ) && ! is_user_member_of_blog( $user->ID ) ) { + if ( is_multisite() && ! current_user_can( 'manage_network_users' ) && ! user_can( $user->ID, 'manage_sites' ) && ! is_user_member_of_blog( $user->ID ) ) { return $error; } diff --git a/tests/phpunit/tests/rest-api/rest-application-passwords-controller.php b/tests/phpunit/tests/rest-api/rest-application-passwords-controller.php index 060a5c0912a94..a1269fe44dcfa 100644 --- a/tests/phpunit/tests/rest-api/rest-application-passwords-controller.php +++ b/tests/phpunit/tests/rest-api/rest-application-passwords-controller.php @@ -1112,4 +1112,43 @@ private function setup_app_password_authenticated_request() { return $item; } + + /** + * Verifies get_user() resolves a user by ID when they are not a member of the main site. + * + * @ticket 60029 + * @group ms-required + */ + public function test_create_item_for_user_without_role_on_main_site() { + if ( ! is_multisite() ) { + $this->markTestSkipped( 'Test only runs in multisite' ); + } + + wp_set_current_user( self::$admin ); + + $blog_id = self::factory()->blog->create(); + $user_id = self::factory()->user->create(); + + // Remove user from main site if they were automatically added. + if ( is_user_member_of_blog( $user_id ) ) { + remove_user_from_blog( $user_id, get_current_blog_id() ); + } + add_user_to_blog( $blog_id, $user_id, 'subscriber' ); + + $this->assertFalse( is_user_member_of_blog( $user_id ) ); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/users/%d/application-passwords', $user_id ) ); + $request->set_body_params( array( 'name' => 'Test App' ) ); + $response = rest_do_request( $request ); + + $data = $response->get_data(); + if ( isset( $data['code'] ) ) { + $this->assertNotSame( 'rest_user_invalid_id', $data['code'], 'get_user() must resolve user by ID when user is not a member of the main site.' ); + } + $this->assertSame( 201, $response->get_status() ); + + $passwords = WP_Application_Passwords::get_user_application_passwords( $user_id ); + $this->assertCount( 1, $passwords ); + $this->assertSame( 'Test App', $passwords[0]['name'] ); + } }