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