Skip to content
Merged
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
30 changes: 25 additions & 5 deletions src/wp-includes/capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -1155,26 +1155,46 @@ function remove_role( $role ) {
* Retrieves a list of super admins.
*
* @since 3.0.0
* @since 7.2.0 Added the `$network_id` parameter.
*
* @global array $super_admins
*
* @param int|null $network_id Optional. ID of the network. Default is the current network.
* @return string[] List of super admin logins.
*/
function get_super_admins() {
function get_super_admins( $network_id = null ) {
global $super_admins;

return $super_admins ?? get_site_option( 'site_admins', array( 'admin' ) );
// Falsy values (including 0) mean "unspecified", matching get_network_option()'s own fallback.
$is_current_network = ! $network_id || (int) $network_id === get_current_network_id();

if ( $is_current_network && isset( $super_admins ) ) {
return $super_admins;
}

/*
* The 'admin' fallback exists for the current network only, where it reflects the
* install's original default super admin. A network whose `site_admins` option was
* never explicitly set has no default super admins - assuming 'admin' there would let
* any user with that login be treated as a super admin of a network they were never
* granted access to.
*/
$default = $is_current_network ? array( 'admin' ) : array();

return get_network_option( $network_id, 'site_admins', $default );
}

/**
* Determines whether user is a site admin.
*
* @since 3.0.0
* @since 7.2.0 Added the `$network_id` parameter.
*
* @param int|false $user_id Optional. The ID of a user. Defaults to false, to check the current user.
* @param int|false $user_id Optional. The ID of a user. Defaults to false, to check the current user.
* @param int|null $network_id Optional. ID of the network. Default is the current network.
* @return bool Whether the user is a site admin.
*/
function is_super_admin( $user_id = false ) {
function is_super_admin( $user_id = false, $network_id = null ) {
if ( ! $user_id ) {
$user = wp_get_current_user();
} else {
Expand All @@ -1186,7 +1206,7 @@ function is_super_admin( $user_id = false ) {
}

if ( is_multisite() ) {
$super_admins = get_super_admins();
$super_admins = get_super_admins( $network_id );
if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins, true ) ) {
return true;
}
Expand Down
114 changes: 113 additions & 1 deletion src/wp-includes/rest-api/endpoints/class-wp-rest-sites-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,30 @@ 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 edit sites.' ), array( 'status' => rest_authorization_required_code() ) );
}

Expand Down Expand Up @@ -408,13 +432,74 @@ public function get_item_permissions_check( $request ) {
return new WP_Error( 'rest_multisite_not_installed', __( 'Multisite is not installed' ), array( 'status' => 400 ) );
}

if ( $site->network_id > 0 ) {
$network_check = $this->check_network_ids_exist( (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;
}
}

if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'manage_sites' ) ) {
return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to view sites.' ), array( 'status' => rest_authorization_required_code() ) );
}

return true;
}

/**
* Validates that all network IDs in a request parameter exist.
*
* @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.
*/
protected function check_network_ids_exist( array $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;
}

foreach ( $network_ids as $network_id ) {
if ( (int) $network_id === $current_network_id ) {
continue;
}

if ( ! is_super_admin( $user_id, $network_id ) ) {
return new WP_Error( 'rest_cannot_view_network', __( 'Sorry, you are not allowed to access sites on this network.' ), array( 'status' => rest_authorization_required_code() ) );
}
}

return true;
}

/**
* Retrieves a site.
*
Expand Down Expand Up @@ -452,6 +537,19 @@ public function create_item_permissions_check( $request ) {
return new WP_Error( 'rest_multisite_not_installed', __( 'Multisite is not installed' ), array( 'status' => 400 ) );
}

$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 );
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;
}
}

return current_user_can( 'create_sites' );
}

Expand Down Expand Up @@ -569,14 +667,28 @@ public function update_item_permissions_check( $request ) {
return $site;
}

if ( 0 === get_current_user_id() ) {
$current_user_id = get_current_user_id();
if ( 0 === $current_user_id ) {
return false;
}

if ( ! is_multisite() ) {
return new WP_Error( 'rest_multisite_not_installed', __( 'Multisite is not installed' ), array( 'status' => 400 ) );
}

$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 );
if ( is_wp_error( $network_check ) ) {
return $network_check;
}

$network_access = $this->check_network_access( $current_user_id, (array) $network_id );
if ( is_wp_error( $network_access ) ) {
return $network_access;
}
}

if ( ! $this->check_edit_permission( $site ) ) {
return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this site.' ), array( 'status' => rest_authorization_required_code() ) );
}
Expand Down
Loading