diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-sites-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-sites-controller.php index feb12673e951e..b3c547d84da25 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-sites-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-sites-controller.php @@ -133,6 +133,16 @@ public function get_items_permissions_check( $request ) { return $multisite_support; } + foreach ( array( 'network', 'network_exclude' ) as $network_key ) { + if ( ! isset( $request[ $network_key ] ) ) { + continue; + } + $network_check = $this->check_network_ids( (array) $request[ $network_key ] ); + if ( is_wp_error( $network_check ) ) { + return $network_check; + } + } + if ( $this->check_edit_permission() ) { return true; } @@ -142,30 +152,6 @@ public function get_items_permissions_check( $request ) { return true; } - if ( isset( $request['network'] ) ) { - $network_check = $this->check_network_ids_exist( (array) $request['network'] ); - if ( is_wp_error( $network_check ) ) { - return $network_check; - } - - $network_access = $this->check_network_access( get_current_user_id(), (array) $request['network'] ); - if ( is_wp_error( $network_access ) ) { - return $network_access; - } - } - - if ( isset( $request['network_exclude'] ) ) { - $network_check = $this->check_network_ids_exist( (array) $request['network_exclude'] ); - if ( is_wp_error( $network_check ) ) { - return $network_check; - } - - $network_access = $this->check_network_access( get_current_user_id(), (array) $request['network_exclude'] ); - if ( is_wp_error( $network_access ) ) { - return $network_access; - } - } - return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to view sites.' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -436,15 +422,10 @@ public function get_item_permissions_check( $request ) { } if ( $site->network_id > 0 ) { - $network_check = $this->check_network_ids_exist( (array) $site->network_id ); + $network_check = $this->check_network_ids( (array) $site->network_id ); if ( is_wp_error( $network_check ) ) { return $network_check; } - - $network_access = $this->check_network_access( get_current_user_id(), (array) $site->network_id ); - if ( is_wp_error( $network_access ) ) { - return $network_access; - } } $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; @@ -461,37 +442,27 @@ public function get_item_permissions_check( $request ) { } /** - * Validates that all network IDs in a request parameter exist. + * Validates that the given network IDs exist and are accessible to the current user. + * + * A user can always access the current network. Access to any other + * network requires the user to be a super admin of that network. * * @since 7.2.0 * * @param int[] $network_ids Array of network IDs. - * @return true|WP_Error True if all network IDs are valid, WP_Error otherwise. + * @return true|WP_Error True if all network IDs exist and are accessible, WP_Error otherwise. */ - protected function check_network_ids_exist( array $network_ids ) { + protected function check_network_ids( array $network_ids ) { + $current_network_id = get_current_network_id(); + $user_id = get_current_user_id(); + + _prime_network_caches( $network_ids ); foreach ( $network_ids as $network_id ) { if ( ! get_network( $network_id ) ) { return new WP_Error( 'rest_network_id_invalid', __( 'Invalid network ID.' ), array( 'status' => 400 ) ); } } - return true; - } - - /** - * Checks if a user can access sites on the given networks. - * - * A user can always access the current network. Access to any other - * network requires the user to be a super admin of that network. - * - * @since 7.2.0 - * - * @param int $user_id User ID. - * @param int[] $network_ids Array of network IDs. - * @return true|WP_Error True if the user can access all given networks, WP_Error otherwise. - */ - protected function check_network_access( $user_id, array $network_ids ) { - $current_network_id = get_current_network_id(); if ( count( $network_ids ) === 1 && $current_network_id === $network_ids[0] ) { return true; } @@ -545,15 +516,10 @@ public function create_item_permissions_check( $request ) { $network_id = isset( $request['network'] ) ? (int) $request['network'] : get_current_network_id(); if ( $network_id > 0 ) { - $network_check = $this->check_network_ids_exist( (array) $network_id ); + $network_check = $this->check_network_ids( (array) $network_id ); if ( is_wp_error( $network_check ) ) { return $network_check; } - - $network_access = $this->check_network_access( get_current_user_id(), (array) $network_id ); - if ( is_wp_error( $network_access ) ) { - return $network_access; - } } if ( ! current_user_can( 'create_sites' ) ) { @@ -683,15 +649,10 @@ public function update_item_permissions_check( $request ) { $network_id = isset( $request['network'] ) ? (int) $request['network'] : $site->network_id; if ( $network_id > 0 ) { - $network_check = $this->check_network_ids_exist( (array) $network_id ); + $network_check = $this->check_network_ids( (array) $network_id ); if ( is_wp_error( $network_check ) ) { return $network_check; } - - $network_access = $this->check_network_access( get_current_user_id(), (array) $network_id ); - if ( is_wp_error( $network_access ) ) { - return $network_access; - } } if ( ! $this->check_edit_permission() ) { @@ -789,6 +750,13 @@ public function delete_item_permissions_check( $request ) { return $site; } + if ( $site->network_id > 0 ) { + $network_check = $this->check_network_ids( (array) $site->network_id ); + if ( is_wp_error( $network_check ) ) { + return $network_check; + } + } + if ( ! $this->check_delete_permission( $site ) ) { return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this site.' ), array( 'status' => rest_authorization_required_code() ) ); } diff --git a/tests/phpunit/tests/rest-api/wpRestSitesController.php b/tests/phpunit/tests/rest-api/wpRestSitesController.php index 3a0e02b286734..753f1a6f79180 100644 --- a/tests/phpunit/tests/rest-api/wpRestSitesController.php +++ b/tests/phpunit/tests/rest-api/wpRestSitesController.php @@ -1999,7 +1999,7 @@ public function test_get_items_filter_with_includes_user() { /** * @ticket 40365 * @covers ::get_items_permissions_check - * @covers ::check_network_ids_exist + * @covers ::check_network_ids * @group ms-required */ public function test_get_items_permissions_check_invalid_network_id() { @@ -2015,7 +2015,7 @@ public function test_get_items_permissions_check_invalid_network_id() { /** * @ticket 40365 * @covers ::get_items_permissions_check - * @covers ::check_network_ids_exist + * @covers ::check_network_ids * @group ms-required */ public function test_get_items_permissions_check_invalid_network_exclude_id() { @@ -2031,7 +2031,7 @@ public function test_get_items_permissions_check_invalid_network_exclude_id() { /** * @ticket 40365 * @covers ::get_items_permissions_check - * @covers ::check_network_access + * @covers ::check_network_ids * @group ms-required */ public function test_get_items_permissions_check_denies_access_to_another_network() { @@ -2048,7 +2048,7 @@ public function test_get_items_permissions_check_denies_access_to_another_networ /** * @ticket 40365 * @covers ::get_items_permissions_check - * @covers ::check_network_access + * @covers ::check_network_ids * @group ms-required */ public function test_get_items_permissions_check_allows_filtering_by_the_current_network() { @@ -2066,7 +2066,7 @@ public function test_get_items_permissions_check_allows_filtering_by_the_current /** * @ticket 40365 * @covers ::create_item_permissions_check - * @covers ::check_network_ids_exist + * @covers ::check_network_ids * @group ms-required */ public function test_create_item_permissions_check_invalid_network_id() { @@ -2084,7 +2084,7 @@ public function test_create_item_permissions_check_invalid_network_id() { /** * @ticket 40365 * @covers ::create_item_permissions_check - * @covers ::check_network_access + * @covers ::check_network_ids * @group ms-required */ public function test_create_item_permissions_check_denies_access_to_another_network() { @@ -2103,7 +2103,7 @@ public function test_create_item_permissions_check_denies_access_to_another_netw /** * @ticket 40365 * @covers ::create_item_permissions_check - * @covers ::check_network_access + * @covers ::check_network_ids * @group ms-required */ public function test_create_item_permissions_check_allows_a_super_admin_of_the_target_network() { @@ -2123,7 +2123,7 @@ public function test_create_item_permissions_check_allows_a_super_admin_of_the_t /** * @ticket 40365 * @covers ::update_item_permissions_check - * @covers ::check_network_ids_exist + * @covers ::check_network_ids * @group ms-required */ public function test_update_item_permissions_check_invalid_network_id() { @@ -2141,7 +2141,7 @@ public function test_update_item_permissions_check_invalid_network_id() { /** * @ticket 40365 * @covers ::update_item_permissions_check - * @covers ::check_network_access + * @covers ::check_network_ids * @group ms-required */ public function test_update_item_permissions_check_denies_moving_a_site_to_another_network() { @@ -2161,7 +2161,7 @@ public function test_update_item_permissions_check_denies_moving_a_site_to_anoth /** * @ticket 40365 * @covers ::update_item_permissions_check - * @covers ::check_network_access + * @covers ::check_network_ids * @group ms-required */ public function test_update_item_permissions_check_allows_the_sites_own_network_by_default() { @@ -2175,10 +2175,73 @@ public function test_update_item_permissions_check_allows_the_sites_own_network_ $this->assertEquals( 200, $response->get_status() ); } + /** + * @ticket 40365 + * @covers ::delete_item_permissions_check + * @covers ::check_network_ids + * @group ms-required + */ + public function test_delete_item_permissions_check_invalid_network_id() { + $blog_id = self::factory()->blog->create(); + + // Orphan the site from its network to simulate an invalid stored network ID. + wp_update_site( $blog_id, array( 'network_id' => REST_TESTS_IMPOSSIBLY_HIGH_NUMBER ) ); + + wp_set_current_user( self::$superadmin_id ); + + $request = new WP_REST_Request( 'DELETE', '/wp/v2/sites/' . $blog_id ); + $request->set_param( 'id', $blog_id ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_network_id_invalid', $response, 400 ); + } + + /** + * @ticket 40365 + * @covers ::delete_item_permissions_check + * @covers ::check_network_ids + * @group ms-required + */ + public function test_delete_item_permissions_check_denies_access_to_another_network() { + $network_id = self::factory()->network->create(); + delete_network_option( $network_id, 'site_admins' ); + $blog_id = self::factory()->blog->create( array( 'site_id' => $network_id ) ); + + wp_set_current_user( self::$superadmin_id ); + + $request = new WP_REST_Request( 'DELETE', '/wp/v2/sites/' . $blog_id ); + $request->set_param( 'id', $blog_id ); + + $response = rest_get_server()->dispatch( $request ); + $this->assertErrorResponse( 'rest_cannot_view_network', $response, 403 ); + } + + /** + * @ticket 40365 + * @covers ::delete_item_permissions_check + * @covers ::check_network_ids + * @group ms-required + */ + public function test_delete_item_permissions_check_allows_a_super_admin_of_the_sites_network() { + $network_id = self::factory()->network->create(); + $user = self::factory()->user->create_and_get(); + update_network_option( $network_id, 'site_admins', array( $user->user_login ) ); + // delete_sites is checked against the current network, so grant it there too. + grant_super_admin( $user->ID ); + $blog_id = self::factory()->blog->create( array( 'site_id' => $network_id ) ); + + wp_set_current_user( $user->ID ); + + $request = new WP_REST_Request( 'DELETE', '/wp/v2/sites/' . $blog_id ); + $request->set_param( 'id', $blog_id ); + + $this->assertTrue( $this->endpoint->delete_item_permissions_check( $request ) ); + } + /** * @ticket 40365 * @covers ::get_item_permissions_check - * @covers ::check_network_access + * @covers ::check_network_ids * @group ms-required */ public function test_get_item_permissions_check_denies_a_site_on_another_network() { @@ -2199,7 +2262,7 @@ public function test_get_item_permissions_check_denies_a_site_on_another_network /** * @ticket 40365 * @covers ::get_item_permissions_check - * @covers ::check_network_access + * @covers ::check_network_ids * @group ms-required */ public function test_get_item_permissions_check_allows_a_site_on_the_current_network() { diff --git a/tests/qunit/fixtures/wp-api-generated.js b/tests/qunit/fixtures/wp-api-generated.js index f54dfc053279d..85cd6b8785df3 100644 --- a/tests/qunit/fixtures/wp-api-generated.js +++ b/tests/qunit/fixtures/wp-api-generated.js @@ -22,13 +22,7 @@ mockedApiResponse.Schema = { "wp-block-editor/v1", "wp-abilities/v1" ], - "authentication": { - "application-passwords": { - "endpoints": { - "authorization": "http://example.org/wp-admin/authorize-application.php" - } - } - }, + "authentication": [], "routes": { "/": { "namespace": "", @@ -11209,7 +11203,6 @@ mockedApiResponse.Schema = { "required": false }, "network": { - "default": 1, "description": "The site's network ID. Default is the current network ID.", "type": "integer", "required": false