Skip to content

Commit 0dc1e06

Browse files
author
flixos90
committed
Multisite: Introduce a can_add_user_to_blog filter to prevent adding a user to a site.
Under certain circumstances, it can be necessary that a user should not be added to a site, beyond the restrictions that WordPress core applies. With the new `can_add_user_to_blog` filter, plugin developers can run custom checks and return an error in case of a failure, that will prevent the user from being added. The user-facing parts and the REST API route that interact with `add_user_to_blog()` have been adjusted accordingly to provide appropriate error feedback when a user could not be added to a site. Furthermore, two existing error feedback messages in the site admin's "New User" screen have been adjusted to properly show inside an error notice instead of a success notice. Props jmdodd. Fixes #41101. git-svn-id: https://develop.svn.wordpress.org/trunk@41225 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 814e1f5 commit 0dc1e06

6 files changed

Lines changed: 146 additions & 33 deletions

File tree

src/wp-admin/network/site-users.php

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,21 @@
6666
if ( false === $user_id ) {
6767
$update = 'err_new_dup';
6868
} else {
69-
add_user_to_blog( $id, $user_id, $_POST['new_role'] );
70-
$update = 'newuser';
71-
/**
72-
* Fires after a user has been created via the network site-users.php page.
73-
*
74-
* @since 4.4.0
75-
*
76-
* @param int $user_id ID of the newly created user.
77-
*/
78-
do_action( 'network_site_users_created_user', $user_id );
69+
$result = add_user_to_blog( $id, $user_id, $_POST['new_role'] );
70+
71+
if ( is_wp_error( $result ) ) {
72+
$update = 'err_add_fail';
73+
} else {
74+
$update = 'newuser';
75+
/**
76+
* Fires after a user has been created via the network site-users.php page.
77+
*
78+
* @since 4.4.0
79+
*
80+
* @param int $user_id ID of the newly created user.
81+
*/
82+
do_action( 'network_site_users_created_user', $user_id );
83+
}
7984
}
8085
}
8186
break;
@@ -87,10 +92,15 @@
8792
$newuser = $_POST['newuser'];
8893
$user = get_user_by( 'login', $newuser );
8994
if ( $user && $user->exists() ) {
90-
if ( ! is_user_member_of_blog( $user->ID, $id ) )
91-
add_user_to_blog( $id, $user->ID, $_POST['new_role'] );
92-
else
95+
if ( ! is_user_member_of_blog( $user->ID, $id ) ) {
96+
$result = add_user_to_blog( $id, $user->ID, $_POST['new_role'] );
97+
98+
if ( is_wp_error( $result ) ) {
99+
$update = 'err_add_fail';
100+
}
101+
} else {
93102
$update = 'err_add_member';
103+
}
94104
} else {
95105
$update = 'err_add_notfound';
96106
}
@@ -223,6 +233,9 @@
223233
case 'err_add_member':
224234
echo '<div id="message" class="error notice is-dismissible"><p>' . __( 'User is already a member of this site.' ) . '</p></div>';
225235
break;
236+
case 'err_add_fail':
237+
echo '<div id="message" class="error notice is-dismissible"><p>' . __( 'User could not be added to this site.' ) . '</p></div>';
238+
break;
226239
case 'err_add_notfound':
227240
echo '<div id="message" class="error notice is-dismissible"><p>' . __( 'Enter the username of an existing user.' ) . '</p></div>';
228241
break;

src/wp-admin/user-new.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,13 @@
6767
$redirect = add_query_arg( array('update' => 'addexisting'), 'user-new.php' );
6868
} else {
6969
if ( isset( $_POST[ 'noconfirmation' ] ) && current_user_can( 'manage_network_users' ) ) {
70-
add_existing_user_to_blog( array( 'user_id' => $user_id, 'role' => $_REQUEST[ 'role' ] ) );
71-
$redirect = add_query_arg( array( 'update' => 'addnoconfirmation' , 'user_id' => $user_id ), 'user-new.php' );
70+
$result = add_existing_user_to_blog( array( 'user_id' => $user_id, 'role' => $_REQUEST[ 'role' ] ) );
71+
72+
if ( ! is_wp_error( $result ) ) {
73+
$redirect = add_query_arg( array( 'update' => 'addnoconfirmation', 'user_id' => $user_id ), 'user-new.php' );
74+
} else {
75+
$redirect = add_query_arg( array( 'update' => 'could_not_add' ), 'user-new.php' );
76+
}
7277
} else {
7378
$newuser_key = substr( md5( $user_id ), 0, 5 );
7479
add_option( 'new_user_' . $newuser_key, array( 'user_id' => $user_id, 'email' => $user_details->user_email, 'role' => $_REQUEST[ 'role' ] ) );
@@ -157,6 +162,8 @@
157162
$new_user = wpmu_activate_signup( $key );
158163
if ( is_wp_error( $new_user ) ) {
159164
$redirect = add_query_arg( array( 'update' => 'addnoconfirmation' ), 'user-new.php' );
165+
} elseif ( ! is_user_member_of_blog( $new_user['user_id'] ) ) {
166+
$redirect = add_query_arg( array( 'update' => 'created_could_not_add' ), 'user-new.php' );
160167
} else {
161168
$redirect = add_query_arg( array( 'update' => 'addnoconfirmation', 'user_id' => $new_user['user_id'] ), 'user-new.php' );
162169
}
@@ -261,11 +268,17 @@
261268
case "addexisting":
262269
$messages[] = __('That user is already a member of this site.');
263270
break;
271+
case "could_not_add":
272+
$add_user_errors = new WP_Error( 'could_not_add', __( 'That user could not be added to this site.' ) );
273+
break;
274+
case "created_could_not_add":
275+
$add_user_errors = new WP_Error( 'created_could_not_add', __( 'User has been created, but could not be added to this site.' ) );
276+
break;
264277
case "does_not_exist":
265-
$messages[] = __('The requested user does not exist.');
278+
$add_user_errors = new WP_Error( 'does_not_exist', __( 'The requested user does not exist.' ) );
266279
break;
267280
case "enter_email":
268-
$messages[] = __('Please enter a valid email address.');
281+
$add_user_errors = new WP_Error( 'enter_email', __( 'Please enter a valid email address.' ) );
269282
break;
270283
}
271284
} else {

src/wp-includes/ms-functions.php

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,12 @@ function get_active_blog_for_user( $user_id ) {
5959
}
6060
} else {
6161
//TODO Review this call to add_user_to_blog too - to get here the user must have a role on this blog?
62-
add_user_to_blog( $first_blog->userblog_id, $user_id, 'subscriber' );
63-
update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
64-
$primary = $first_blog;
62+
$result = add_user_to_blog( $first_blog->userblog_id, $user_id, 'subscriber' );
63+
64+
if ( ! is_wp_error( $result ) ) {
65+
update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
66+
$primary = $first_blog;
67+
}
6568
}
6669

6770
if ( ( ! is_object( $primary ) ) || ( $primary->archived == 1 || $primary->spam == 1 || $primary->deleted == 1 ) ) {
@@ -160,6 +163,29 @@ function add_user_to_blog( $blog_id, $user_id, $role ) {
160163
return new WP_Error( 'user_does_not_exist', __( 'The requested user does not exist.' ) );
161164
}
162165

166+
/**
167+
* Filters whether a user should be added to a site.
168+
*
169+
* @since 4.9.0
170+
*
171+
* @param bool|WP_Error $retval True if the user should be added to the site, false
172+
* or error object otherwise.
173+
* @param int $user_id User ID.
174+
* @param string $role User role.
175+
* @param int $blog_id Site ID.
176+
*/
177+
$can_add_user = apply_filters( 'can_add_user_to_blog', true, $user_id, $role, $blog_id );
178+
179+
if ( true !== $can_add_user ) {
180+
restore_current_blog();
181+
182+
if ( is_wp_error( $can_add_user ) ) {
183+
return $can_add_user;
184+
}
185+
186+
return new WP_Error( 'user_cannot_be_added', __( 'User cannot be added to this site.' ) );
187+
}
188+
163189
if ( !get_user_meta($user_id, 'primary_blog', true) ) {
164190
update_user_meta($user_id, 'primary_blog', $blog_id);
165191
$site = get_site( $blog_id );
@@ -2081,15 +2107,19 @@ function add_existing_user_to_blog( $details = false ) {
20812107
if ( is_array( $details ) ) {
20822108
$blog_id = get_current_blog_id();
20832109
$result = add_user_to_blog( $blog_id, $details[ 'user_id' ], $details[ 'role' ] );
2084-
/**
2085-
* Fires immediately after an existing user is added to a site.
2086-
*
2087-
* @since MU (3.0.0)
2088-
*
2089-
* @param int $user_id User ID.
2090-
* @param mixed $result True on success or a WP_Error object if the user doesn't exist.
2091-
*/
2092-
do_action( 'added_existing_user', $details['user_id'], $result );
2110+
2111+
if ( ! is_wp_error( $result ) ) {
2112+
/**
2113+
* Fires immediately after an existing user is added to a site.
2114+
*
2115+
* @since MU (3.0.0)
2116+
*
2117+
* @param int $user_id User ID.
2118+
* @param mixed $result True on success or a WP_Error object if the user doesn't exist.
2119+
*/
2120+
do_action( 'added_existing_user', $details['user_id'], $result );
2121+
}
2122+
20932123
return $result;
20942124
}
20952125
}
@@ -2111,9 +2141,13 @@ function add_new_user_to_blog( $user_id, $password, $meta ) {
21112141
if ( !empty( $meta[ 'add_to_blog' ] ) ) {
21122142
$blog_id = $meta[ 'add_to_blog' ];
21132143
$role = $meta[ 'new_role' ];
2114-
remove_user_from_blog($user_id, get_network()->site_id); // remove user from main blog.
2115-
add_user_to_blog( $blog_id, $user_id, $role );
2116-
update_user_meta( $user_id, 'primary_blog', $blog_id );
2144+
remove_user_from_blog( $user_id, get_network()->site_id ); // remove user from main blog.
2145+
2146+
$result = add_user_to_blog( $blog_id, $user_id, $role );
2147+
2148+
if ( ! is_wp_error( $result ) ) {
2149+
update_user_meta( $user_id, 'primary_blog', $blog_id );
2150+
}
21172151
}
21182152
}
21192153

src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,10 @@ public function create_item( $request ) {
492492
return $user_id;
493493
}
494494

495-
add_user_to_blog( get_site()->id, $user_id, '' );
495+
$result= add_user_to_blog( get_site()->id, $user_id, '' );
496+
if ( is_wp_error( $result ) ) {
497+
return $result;
498+
}
496499
} else {
497500
$user_id = wp_insert_user( wp_slash( (array) $user ) );
498501

tests/phpunit/tests/rest-api/rest-users-controller.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,30 @@ public function test_create_new_network_user_on_site_does_not_add_user_to_sub_si
10201020
$this->assertFalse( $user_is_member );
10211021
}
10221022

1023+
/**
1024+
* @ticket 41101
1025+
* @group ms-required
1026+
*/
1027+
public function test_create_new_network_user_with_add_user_to_blog_failure() {
1028+
$this->allow_user_to_manage_multisite();
1029+
1030+
$params = array(
1031+
'username' => 'testuser123',
1032+
'password' => 'testpassword',
1033+
'email' => 'test@example.com',
1034+
'name' => 'Test User 123',
1035+
'roles' => array( 'editor' ),
1036+
);
1037+
1038+
add_filter( 'can_add_user_to_blog', '__return_false' );
1039+
1040+
$request = new WP_REST_Request( 'POST', '/wp/v2/users' );
1041+
$request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
1042+
$request->set_body_params( $params );
1043+
$response = $this->server->dispatch( $request );
1044+
$this->assertErrorResponse( 'user_cannot_be_added', $response );
1045+
}
1046+
10231047
/**
10241048
* @group ms-required
10251049
*/

tests/phpunit/tests/user/multisite.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,32 @@ public function test_add_user_to_blog_invalid_user() {
397397
$this->assertWPError( $result );
398398
}
399399

400+
/**
401+
* @ticket 41101
402+
*/
403+
public function test_should_fail_can_add_user_to_blog_filter() {
404+
$site_id = self::factory()->blog->create();
405+
$user_id = self::factory()->user->create();
406+
407+
add_filter( 'can_add_user_to_blog', '__return_false' );
408+
$result = add_user_to_blog( $site_id, $user_id, 'subscriber' );
409+
410+
$this->assertWPError( $result );
411+
}
412+
413+
/**
414+
* @ticket 41101
415+
*/
416+
public function test_should_succeed_can_add_user_to_blog_filter() {
417+
$site_id = self::factory()->blog->create();
418+
$user_id = self::factory()->user->create();
419+
420+
add_filter( 'can_add_user_to_blog', '__return_true' );
421+
$result = add_user_to_blog( $site_id, $user_id, 'subscriber' );
422+
423+
$this->assertTrue( $result );
424+
}
425+
400426
/**
401427
* @ticket 23016
402428
*/

0 commit comments

Comments
 (0)