PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 7.3.4
Jetpack – WP Security, Backup, Speed, & Growth v7.3.4
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / sync / class.jetpack-sync-module-users.php
class.jetpack-sync-module-users.php
501 lines 15.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class Jetpack_Sync_Module_Users extends Jetpack_Sync_Module {
4 const MAX_INITIAL_SYNC_USERS = 100;
5
6 protected $flags = array();
7
8 function name() {
9 return 'users';
10 }
11
12 // this is here to support the backfill API
13 public function get_object_by_id( $object_type, $id ) {
14 if ( $object_type === 'user' && $user = get_user_by( 'id', intval( $id ) ) ) {
15 return $this->sanitize_user_and_expand( $user );
16 }
17
18 return false;
19 }
20
21 public function init_listeners( $callable ) {
22
23 // users
24 add_action( 'user_register', array( $this, 'user_register_handler' ) );
25 add_action( 'profile_update', array( $this, 'save_user_handler' ), 10, 2 );
26
27 add_action( 'add_user_to_blog', array( $this, 'add_user_to_blog_handler' ) );
28 add_action( 'jetpack_sync_add_user', $callable, 10, 2 );
29
30 add_action( 'jetpack_sync_register_user', $callable, 10, 2 );
31 add_action( 'jetpack_sync_save_user', $callable, 10, 2 );
32
33 add_action( 'jetpack_sync_user_locale', $callable, 10, 2 );
34 add_action( 'jetpack_sync_user_locale_delete', $callable, 10, 1 );
35
36 add_action( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 );
37 add_action( 'jetpack_deleted_user', $callable, 10, 3 );
38 add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 );
39 add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 );
40
41 // user roles
42 add_action( 'add_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
43 add_action( 'set_user_role', array( $this, 'save_user_role_handler' ), 10, 3 );
44 add_action( 'remove_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
45
46 // user capabilities
47 add_action( 'added_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
48 add_action( 'updated_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
49 add_action( 'deleted_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
50
51 // user authentication
52 add_filter( 'authenticate', array( $this, 'authenticate_handler' ), 1000, 3 );
53 add_action( 'wp_login', array( $this, 'wp_login_handler' ), 10, 2 );
54
55 add_action( 'jetpack_wp_login', $callable, 10, 3 );
56
57 add_action( 'wp_logout', $callable, 10, 0 );
58
59 // Add on init
60 add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_add_user', array( $this, 'expand_action' ) );
61 add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_register_user', array( $this, 'expand_action' ) );
62 add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_save_user', array( $this, 'expand_action' ) );
63 }
64
65 public function init_full_sync_listeners( $callable ) {
66 add_action( 'jetpack_full_sync_users', $callable );
67 }
68
69 public function init_before_send() {
70
71 add_filter( 'jetpack_sync_before_send_jetpack_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
72 add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
73
74 // full sync
75 add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
76 }
77
78 private function get_user( $user ) {
79 if ( is_numeric( $user ) ) {
80 $user = get_user_by( 'id', $user );
81 }
82 if ( $user instanceof WP_User ) {
83 return $user;
84 }
85 return null;
86 }
87
88 public function sanitize_user( $user ) {
89 $user = $this->get_user( $user );
90 // this create a new user object and stops the passing of the object by reference.
91 $user = unserialize( serialize( $user ) );
92
93 if ( is_object( $user ) && is_object( $user->data ) ) {
94 unset( $user->data->user_pass );
95 }
96 return $user;
97 }
98
99 public function expand_user( $user ) {
100 if ( ! is_object( $user ) ) {
101 return null;
102 }
103 $user->allowed_mime_types = get_allowed_mime_types( $user );
104 $user->allcaps = $this->get_real_user_capabilities( $user );
105
106 // Only set the user locale if it is different from the site local
107 if ( get_locale() !== get_user_locale( $user->ID ) ) {
108 $user->locale = get_user_locale( $user->ID );
109 }
110
111 return $user;
112 }
113
114 public function get_real_user_capabilities( $user ) {
115 $user_capabilities = array();
116 if ( is_wp_error( $user ) ) {
117 return $user_capabilities;
118 }
119 foreach ( Jetpack_Sync_Defaults::get_capabilities_whitelist() as $capability ) {
120 if ( $user_has_capabilities = user_can( $user, $capability ) ) {
121 $user_capabilities[ $capability ] = true;
122 }
123 }
124 return $user_capabilities;
125 }
126
127 public function sanitize_user_and_expand( $user ) {
128 $user = $this->get_user( $user );
129 $user = $this->expand_user( $user );
130 return $this->sanitize_user( $user );
131 }
132
133 public function expand_action( $args ) {
134 // the first argument is always the user
135 list( $user ) = $args;
136 if ( $user ) {
137 $args[0] = $this->sanitize_user_and_expand( $user );
138 return $args;
139 }
140
141 return false;
142 }
143
144 public function expand_login_username( $args ) {
145 list( $login, $user, $flags ) = $args;
146 $user = $this->sanitize_user( $user );
147
148 return array( $login, $user, $flags );
149 }
150
151 public function expand_logout_username( $args, $user_id ) {
152 $user = get_userdata( $user_id );
153 $user = $this->sanitize_user( $user );
154
155 $login = '';
156 if ( is_object( $user ) && is_object( $user->data ) ) {
157 $login = $user->data->user_login;
158 }
159 // if we don't have a user here lets not send anything.
160 if ( empty( $login ) ) {
161 return false;
162 }
163
164 return array( $login, $user );
165 }
166
167 /**
168 * Additional processing is needed for wp_login so we introduce this wrapper
169 * handler.
170 *
171 * @param String $user_login the user login.
172 * @param WP_User $user the user object.
173 */
174 function wp_login_handler( $user_login, $user ) {
175 /**
176 * Fires when a user is logged into a site.
177 *
178 * @since 7.2.0
179 *
180 * @param Numeric $user_id The user ID.
181 * @param WP_User $user The User Object of the user that currently logged in
182 * @param Array $params Any Flags that have been added during login
183 */
184 do_action( 'jetpack_wp_login', $user->ID, $user, $this->get_flags( $user->ID ) );
185 $this->clear_flags( $user->ID );
186 }
187
188 /**
189 * A hook for the authenticate event that checks the password strength.
190 *
191 * @param WP_Error|WP_User $user the user object, or an error.
192 * @param String $username the username.
193 * @param Sting $password the password used to authenticate.
194 * @return WP_Error|WP_User the same object that was passed into the function.
195 */
196 public function authenticate_handler( $user, $username, $password ) {
197 // In case of cookie authentication we don't do anything here.
198 if ( empty( $password ) ) {
199 return $user;
200 }
201
202 // We are only interested in successful authentication events.
203 if ( is_wp_error( $user ) || ! ( $user instanceof WP_User ) ) {
204 return $user;
205 }
206
207 jetpack_require_lib( 'class.jetpack-password-checker' );
208 $password_checker = new Jetpack_Password_Checker( $user->ID );
209
210 $test_results = $password_checker->test( $password, true );
211
212 // If the password passes tests, we don't do anything.
213 if ( empty( $test_results['test_results']['failed'] ) ) {
214 return $user;
215 }
216
217 $this->add_flags(
218 $user->ID,
219 array(
220 'warning' => 'The password failed at least one strength test.',
221 'failures' => $test_results['test_results']['failed'],
222 )
223 );
224
225 return $user;
226 }
227
228 public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) {
229 $is_multisite = is_multisite();
230 /**
231 * Fires when a user is deleted on a site
232 *
233 * @since 5.4.0
234 *
235 * @param int $deleted_user_id - ID of the deleted user
236 * @param int $reassigned_user_id - ID of the user the deleted user's posts is reassigned to (if any)
237 * @param bool $is_multisite - Whether this site is a multisite installation
238 */
239 do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite );
240 }
241
242 function user_register_handler( $user_id, $old_user_data = null ) {
243 // ensure we only sync users who are members of the current blog
244 if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
245 return;
246 }
247
248 if ( Jetpack_Constants::is_true( 'JETPACK_INVITE_ACCEPTED' ) ) {
249 $this->add_flags( $user_id, array( 'invitation_accepted' => true ) );
250 }
251 /**
252 * Fires when a new user is registered on a site
253 *
254 * @since 4.9.0
255 *
256 * @param object The WP_User object
257 */
258 do_action( 'jetpack_sync_register_user', $user_id, $this->get_flags( $user_id ) );
259 $this->clear_flags( $user_id );
260
261 }
262
263 function add_user_to_blog_handler( $user_id, $old_user_data = null ) {
264 // ensure we only sync users who are members of the current blog
265 if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
266 return;
267 }
268
269 if ( Jetpack_Constants::is_true( 'JETPACK_INVITE_ACCEPTED' ) ) {
270 $this->add_flags( $user_id, array( 'invitation_accepted' => true ) );
271 }
272 /**
273 * Fires when a user is added on a site
274 *
275 * @since 4.9.0
276 *
277 * @param object The WP_User object
278 */
279 do_action( 'jetpack_sync_add_user', $user_id, $this->get_flags( $user_id ) );
280 $this->clear_flags( $user_id );
281 }
282
283 function save_user_handler( $user_id, $old_user_data = null ) {
284 // ensure we only sync users who are members of the current blog
285 if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
286 return;
287 }
288
289 $user = get_user_by( 'id', $user_id );
290
291 // Older versions of WP don't pass the old_user_data in ->data
292 if ( isset( $old_user_data->data ) ) {
293 $old_user = $old_user_data->data;
294 } else {
295 $old_user = $old_user_data;
296 }
297
298 if ( $old_user !== null && $user->user_pass !== $old_user->user_pass ) {
299 $this->flags[ $user_id ]['password_changed'] = true;
300 }
301 if ( $old_user !== null && $user->data->user_email !== $old_user->user_email ) {
302 // The '_new_email' user meta is deleted right after the call to wp_update_user
303 // that got us to this point so if it's still set then this was a user confirming
304 // their new email address
305 if ( 1 === intval( get_user_meta( $user->ID, '_new_email', true ) ) ) {
306 $this->flags[ $user_id ]['email_changed'] = true;
307 }
308 }
309
310 /**
311 * Fires when the client needs to sync an updated user
312 *
313 * @since 4.2.0
314 *
315 * @param object The WP_User object
316 * @param array state - New since 5.8.0
317 */
318 do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
319 $this->clear_flags( $user_id );
320 }
321
322 function save_user_role_handler( $user_id, $role, $old_roles = null ) {
323 $this->add_flags(
324 $user_id,
325 array(
326 'role_changed' => true,
327 'previous_role' => $old_roles,
328 )
329 );
330
331 // The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both
332 if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
333 return;
334 }
335 /**
336 * This action is documented already in this file
337 */
338 do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
339 $this->clear_flags( $user_id );
340 }
341
342 function get_flags( $user_id ) {
343 if ( isset( $this->flags[ $user_id ] ) ) {
344 return $this->flags[ $user_id ];
345 }
346 return array();
347 }
348
349 function clear_flags( $user_id ) {
350 if ( isset( $this->flags[ $user_id ] ) ) {
351 unset( $this->flags[ $user_id ] );
352 }
353 }
354
355 function add_flags( $user_id, $flags ) {
356 $this->flags[ $user_id ] = wp_parse_args( $flags, $this->get_flags( $user_id ) );
357 }
358
359 function maybe_save_user_meta( $meta_id, $user_id, $meta_key, $value ) {
360 if ( $meta_key === 'locale' ) {
361 $this->add_flags( $user_id, array( 'locale_changed' => true ) );
362 }
363
364 $user = get_user_by( 'id', $user_id );
365 if ( isset( $user->cap_key ) && $meta_key === $user->cap_key ) {
366 $this->add_flags( $user_id, array( 'capabilities_changed' => true ) );
367 }
368
369 if ( $this->is_create_user() || $this->is_add_user_to_blog() || $this->is_delete_user() ) {
370 return;
371 }
372
373 if ( isset( $this->flags[ $user_id ] ) ) {
374 /**
375 * This action is documented already in this file
376 */
377 do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
378 }
379 }
380
381 public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
382 global $wpdb;
383
384 return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_users', $wpdb->usermeta, 'user_id', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
385 }
386
387 public function estimate_full_sync_actions( $config ) {
388 global $wpdb;
389
390 $query = "SELECT count(*) FROM $wpdb->usermeta";
391
392 if ( $where_sql = $this->get_where_sql( $config ) ) {
393 $query .= ' WHERE ' . $where_sql;
394 }
395
396 $count = $wpdb->get_var( $query );
397
398 return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
399 }
400
401 private function get_where_sql( $config ) {
402 global $wpdb;
403
404 $query = "meta_key = '{$wpdb->prefix}capabilities'";
405
406 // config is a list of user IDs to sync
407 if ( is_array( $config ) ) {
408 $query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
409 }
410
411 return $query;
412 }
413
414 function get_full_sync_actions() {
415 return array( 'jetpack_full_sync_users' );
416 }
417
418 function get_initial_sync_user_config() {
419 global $wpdb;
420
421 $user_ids = $wpdb->get_col( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}user_level' AND meta_value > 0 LIMIT " . ( self::MAX_INITIAL_SYNC_USERS + 1 ) );
422
423 if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) {
424 return $user_ids;
425 } else {
426 return false;
427 }
428 }
429
430 public function expand_users( $args ) {
431 list( $user_ids, $previous_end ) = $args;
432
433 return array(
434 'users' => array_map( array( $this, 'sanitize_user_and_expand' ), get_users(
435 array(
436 'include' => $user_ids,
437 'orderby' => 'ID',
438 'order' => 'DESC'
439 )
440 ) ),
441 'previous_end' => $previous_end
442 );
443 }
444
445 public function remove_user_from_blog_handler( $user_id, $blog_id ) {
446 // User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114
447 if ( $this->is_add_new_user_to_blog() ) {
448 return;
449 }
450
451 $reassigned_user_id = $this->get_reassigned_network_user_id();
452
453 // Note that we are in the context of the blog the user is removed from, see https://github.com/WordPress/WordPress/blob/473e1ba73bc5c18c72d7f288447503713d518790/wp-includes/ms-functions.php#L233
454 /**
455 * Fires when a user is removed from a blog on a multisite installation
456 *
457 * @since 5.4.0
458 *
459 * @param int $user_id - ID of the removed user
460 * @param int $reassigned_user_id - ID of the user the removed user's posts is reassigned to (if any)
461 */
462 do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id );
463 }
464
465 protected function is_add_new_user_to_blog() {
466 return Jetpack::is_function_in_backtrace( 'add_new_user_to_blog' );
467 }
468
469 protected function is_add_user_to_blog() {
470 return Jetpack::is_function_in_backtrace( 'add_user_to_blog' );
471 }
472
473 protected function is_delete_user() {
474 return Jetpack::is_function_in_backtrace( array( 'wp_delete_user', 'remove_user_from_blog' ) );
475 }
476
477 protected function is_create_user() {
478 $functions = array(
479 'add_new_user_to_blog', // Used to suppress jetpack_sync_save_user in save_user_cap_handler when user registered on multi site
480 'wp_create_user', // Used to suppress jetpack_sync_save_user in save_user_role_handler when user registered on multi site
481 'wp_insert_user', // Used to suppress jetpack_sync_save_user in save_user_cap_handler and save_user_role_handler when user registered on single site
482 );
483
484 return Jetpack::is_function_in_backtrace( $functions );
485 }
486
487 protected function get_reassigned_network_user_id() {
488 $backtrace = debug_backtrace( false ); // phpcs:ignore PHPCompatibility.PHP.NewFunctionParameters.debug_backtrace_optionsFound
489 foreach ( $backtrace as $call ) {
490 if (
491 'remove_user_from_blog' === $call['function'] &&
492 3 === count( $call['args'] )
493 ) {
494 return $call['args'][2];
495 }
496 }
497
498 return false;
499 }
500 }
501