Index: src/wp-includes/ms-functions.php
===================================================================
--- src/wp-includes/ms-functions.php	(revision 40916)
+++ src/wp-includes/ms-functions.php	(working copy)
@@ -160,6 +160,11 @@
 		return new WP_Error( 'user_does_not_exist', __( 'The requested user does not exist.' ) );
 	}
 
+	if ( ! apply_filters( 'pre_add_user_to_blog', true, $user_id, $role, $blog_id ) ) {
+		restore_current_blog();
+		return new WP_Error( 'user_cannot_be_added', __( 'The user cannot be added to the blog.' ) );
+	}
+
 	if ( !get_user_meta($user_id, 'primary_blog', true) ) {
 		update_user_meta($user_id, 'primary_blog', $blog_id);
 		$site = get_site( $blog_id );
Index: tests/phpunit/tests/user/multisite.php
===================================================================
--- tests/phpunit/tests/user/multisite.php	(revision 40916)
+++ tests/phpunit/tests/user/multisite.php	(working copy)
@@ -398,6 +398,34 @@
 	}
 
 	/**
+	 * @ticket 41101
+	 */
+	public function test_should_fail_pre_add_user_to_blog_filter() {
+		$site_id = self::factory()->blog->create();
+		$user_id = self::factory()->user->create();
+
+		add_filter( 'pre_add_user_to_blog', '__return_false', 10, 4 );
+		$result = add_user_to_blog( $site_id, $user_id, 'subscriber' );
+		wpmu_delete_blog( $site_id );
+
+		$this->assertWPError( $result );
+	}
+
+	/**
+	 * @ticket 41101
+	 */
+	public function test_should_succeed_pre_add_user_to_blog_filter() {
+		$site_id = self::factory()->blog->create();
+		$user_id = self::factory()->user->create();
+
+		add_filter( 'pre_add_user_to_blog', '__return_true', 10, 4 );
+		$result = add_user_to_blog( $site_id, $user_id, 'subscriber' );
+		wpmu_delete_blog( $site_id );
+
+		$this->assertTrue( $result );
+	}
+
+	/**
 	 * @ticket 23016
 	 */
 	public function test_wp_roles_global_is_reset() {
