PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 6.9
Jetpack – WP Security, Backup, Speed, & Growth v6.9
16.2-beta 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 All 501 releases
jetpack / sync / class.jetpack-sync-module-users.php

class.jetpack-sync-module-users.php in Jetpack – WP Security, Backup, Speed, & Growth 6.9, at sync/class.jetpack-sync-module-users.php

423 lines 13.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 add_action( 'jetpack_sync_add_user', array( $this, 'clear_flags' ), 11 );
30
31 add_action( 'jetpack_sync_register_user', $callable, 10, 2 );
32 add_action( 'jetpack_sync_register_user', array( $this, 'clear_flags' ), 11 );
33
34 add_action( 'jetpack_sync_save_user', $callable, 10, 2 );
35 add_action( 'jetpack_sync_save_user', array( $this, 'clear_flags' ), 11 );
36
37 add_action( 'jetpack_sync_user_locale', $callable, 10, 2 );
38 add_action( 'jetpack_sync_user_locale_delete', $callable, 10, 1 );
39
40 add_action( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 );
41 add_action( 'jetpack_deleted_user', $callable, 10, 3 );
42 add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 );
43 add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 );
44
45 // user roles
46 add_action( 'add_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
47 add_action( 'set_user_role', array( $this, 'save_user_role_handler' ), 10, 3 );
48 add_action( 'remove_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
49
50 // user capabilities
51 add_action( 'added_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
52 add_action( 'updated_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
53 add_action( 'deleted_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
54
55 // user authentication
56 add_action( 'wp_login', $callable, 10, 2 );
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
73 add_filter( 'jetpack_sync_before_send_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
74 add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
75
76 // full sync
77 add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
78 }
79
80 private function get_user( $user ) {
81 if ( is_numeric( $user ) ) {
82 $user = get_user_by( 'id', $user );
83 }
84 if ( $user instanceof WP_User ) {
85 return $user;
86 }
87 return null;
88 }
89
90 public function sanitize_user( $user ) {
91 $user = $this->get_user( $user );
92 // this create a new user object and stops the passing of the object by reference.
93 $user = unserialize( serialize( $user ) );
94
95 if ( is_object( $user ) && is_object( $user->data ) ) {
96 unset( $user->data->user_pass );
97 }
98 return $user;
99 }
100
101 public function expand_user( $user ) {
102 if ( ! is_object( $user ) ) {
103 return null;
104 }
105 $user->allowed_mime_types = get_allowed_mime_types( $user );
106 $user->allcaps = $this->get_real_user_capabilities( $user );
107
108 // Only set the user locale if it is different from the site local
109 if ( get_locale() !== get_user_locale( $user->ID ) ) {
110 $user->locale = get_user_locale( $user->ID );
111 }
112
113 return $user;
114 }
115
116 public function get_real_user_capabilities( $user ) {
117 $user_capabilities = array();
118 if ( is_wp_error( $user ) ) {
119 return $user_capabilities;
120 }
121 foreach( Jetpack_Sync_Defaults::get_capabilities_whitelist() as $capability ) {
122 if ( $user_has_capabilities = user_can( $user , $capability ) ) {
123 $user_capabilities[ $capability ] = true;
124 }
125 }
126 return $user_capabilities;
127 }
128
129 public function sanitize_user_and_expand( $user ) {
130 $user = $this->get_user( $user );
131 $user = $this->expand_user( $user );
132 return $this->sanitize_user( $user );
133 }
134
135 public function expand_action( $args ) {
136 // the first argument is always the user
137 list( $user ) = $args;
138 if ( $user ) {
139 $args[0] = $this->sanitize_user_and_expand( $user );
140 return $args;
141 }
142
143 return false;
144 }
145
146 public function expand_login_username( $args ) {
147 list( $login, $user ) = $args;
148 $user = $this->sanitize_user( $user );
149
150 return array( $login, $user );
151 }
152
153 public function expand_logout_username( $args, $user_id ) {
154 $user = get_userdata( $user_id );
155 $user = $this->sanitize_user( $user );
156
157 $login = '';
158 if ( is_object( $user ) && is_object( $user->data ) ) {
159 $login = $user->data->user_login;
160 }
161 // if we don't have a user here lets not send anything.
162 if ( empty( $login ) ) {
163 return false;
164 }
165
166 return array( $login, $user );
167 }
168
169 public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) {
170 $is_multisite = is_multisite();
171 /**
172 * Fires when a user is deleted on a site
173 *
174 * @since 5.4.0
175 *
176 * @param int $deleted_user_id - ID of the deleted user
177 * @param int $reassigned_user_id - ID of the user the deleted user's posts is reassigned to (if any)
178 * @param bool $is_multisite - Whether this site is a multisite installation
179 */
180 do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite );
181 }
182
183 function user_register_handler( $user_id, $old_user_data = null ) {
184 // ensure we only sync users who are members of the current blog
185 if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
186 return;
187 }
188
189 if ( Jetpack_Constants::is_true( 'JETPACK_INVITE_ACCEPTED' ) ) {
190 $this->add_flags( $user_id, array( 'invitation_accepted' => true ) );
191 }
192 /**
193 * Fires when a new user is registered on a site
194 *
195 * @since 4.9.0
196 *
197 * @param object The WP_User object
198 */
199 do_action( 'jetpack_sync_register_user', $user_id, $this->get_flags( $user_id ) );
200
201 }
202
203 function add_user_to_blog_handler( $user_id, $old_user_data = null ) {
204 // ensure we only sync users who are members of the current blog
205 if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
206 return;
207 }
208
209 if ( Jetpack_Constants::is_true( 'JETPACK_INVITE_ACCEPTED' ) ) {
210 $this->add_flags( $user_id, array( 'invitation_accepted' => true ) );
211 }
212 /**
213 * Fires when a user is added on a site
214 *
215 * @since 4.9.0
216 *
217 * @param object The WP_User object
218 */
219 do_action( 'jetpack_sync_add_user', $user_id, $this->get_flags( $user_id ) );
220 }
221
222 function save_user_handler( $user_id, $old_user_data = null ) {
223 // ensure we only sync users who are members of the current blog
224 if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
225 return;
226 }
227
228 $user = get_user_by( 'id', $user_id );
229
230 // Older versions of WP don't pass the old_user_data in ->data
231 if ( isset( $old_user_data->data ) ) {
232 $old_user = $old_user_data->data;
233 } else {
234 $old_user = $old_user_data;
235 }
236
237 if ( $old_user !== null && $user->user_pass !== $old_user->user_pass ) {
238 $this->flags[ $user_id ]['password_changed'] = true;
239 }
240 if ( $old_user !== null && $user->data->user_email !== $old_user->user_email ) {
241 // The '_new_email' user meta is deleted right after the call to wp_update_user
242 // that got us to this point so if it's still set then this was a user confirming
243 // their new email address
244 if ( 1 === intval( get_user_meta( $user->ID, '_new_email', true ) ) ) {
245 $this->flags[ $user_id ]['email_changed'] = true;
246 }
247 }
248
249 /**
250 * Fires when the client needs to sync an updated user
251 *
252 * @since 4.2.0
253 *
254 * @param object The WP_User object
255 * @param array state - New since 5.8.0
256 */
257 do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
258 }
259
260 function save_user_role_handler( $user_id, $role, $old_roles = null ) {
261 $this->add_flags( $user_id, array( 'role_changed' => true, 'previous_role' => $old_roles ) );
262
263 //The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both
264 if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
265 return;
266 }
267 /**
268 * This action is documented already in this file
269 */
270 do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
271 }
272
273 function get_flags( $user_id ) {
274 if ( isset( $this->flags[ $user_id ] ) ) {
275 return $this->flags[ $user_id ];
276 }
277 return array();
278 }
279
280 function clear_flags( $user_id ) {
281 if ( isset( $this->flags[ $user_id ] ) ) {
282 unset( $this->flags[ $user_id ] );
283 }
284 }
285
286 function add_flags( $user_id, $flags ) {
287 $this->flags[ $user_id ] = wp_parse_args( $flags, $this->get_flags( $user_id ) );
288 }
289
290 function maybe_save_user_meta( $meta_id, $user_id, $meta_key, $value ) {
291 if ( $meta_key === 'locale' ) {
292 $this->add_flags( $user_id, array( 'locale_changed' => true ) );
293 }
294
295 $user = get_user_by( 'id', $user_id );
296 if ( isset( $user->cap_key ) && $meta_key === $user->cap_key ) {
297 $this->add_flags( $user_id, array( 'capabilities_changed' => true ) );
298 }
299
300 if ( $this->is_create_user() || $this->is_add_user_to_blog() || $this->is_delete_user() ) {
301 return;
302 }
303
304 if ( isset( $this->flags[ $user_id ] ) ) {
305 /**
306 * This action is documented already in this file
307 */
308 do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
309 }
310 }
311
312 public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
313 global $wpdb;
314
315 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 );
316 }
317
318 public function estimate_full_sync_actions( $config ) {
319 global $wpdb;
320
321 $query = "SELECT count(*) FROM $wpdb->usermeta";
322
323 if ( $where_sql = $this->get_where_sql( $config ) ) {
324 $query .= ' WHERE ' . $where_sql;
325 }
326
327 $count = $wpdb->get_var( $query );
328
329 return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
330 }
331
332 private function get_where_sql( $config ) {
333 global $wpdb;
334
335 $query = "meta_key = '{$wpdb->prefix}capabilities'";
336
337 // config is a list of user IDs to sync
338 if ( is_array( $config ) ) {
339 $query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
340 }
341
342 return $query;
343 }
344
345 function get_full_sync_actions() {
346 return array( 'jetpack_full_sync_users' );
347 }
348
349 function get_initial_sync_user_config() {
350 global $wpdb;
351
352 $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 ) );
353
354 if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) {
355 return $user_ids;
356 } else {
357 return false;
358 }
359 }
360
361 public function expand_users( $args ) {
362 $user_ids = $args[0];
363
364 return array_map( array( $this, 'sanitize_user_and_expand' ), get_users( array( 'include' => $user_ids ) ) );
365 }
366
367 public function remove_user_from_blog_handler( $user_id, $blog_id ) {
368 //User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114
369 if ( $this->is_add_new_user_to_blog() ) {
370 return;
371 }
372
373 $reassigned_user_id = $this->get_reassigned_network_user_id();
374
375 //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
376 /**
377 * Fires when a user is removed from a blog on a multisite installation
378 *
379 * @since 5.4.0
380 *
381 * @param int $user_id - ID of the removed user
382 * @param int $reassigned_user_id - ID of the user the removed user's posts is reassigned to (if any)
383 */
384 do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id );
385 }
386
387 protected function is_add_new_user_to_blog() {
388 return Jetpack::is_function_in_backtrace( 'add_new_user_to_blog' );
389 }
390
391 protected function is_add_user_to_blog() {
392 return Jetpack::is_function_in_backtrace( 'add_user_to_blog' );
393 }
394
395 protected function is_delete_user() {
396 return Jetpack::is_function_in_backtrace( array( 'wp_delete_user' , 'remove_user_from_blog' ) );
397 }
398
399 protected function is_create_user() {
400 $functions = array(
401 'add_new_user_to_blog', // Used to suppress jetpack_sync_save_user in save_user_cap_handler when user registered on multi site
402 'wp_create_user', // Used to suppress jetpack_sync_save_user in save_user_role_handler when user registered on multi site
403 '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
404 );
405
406 return Jetpack::is_function_in_backtrace( $functions );
407 }
408
409 protected function get_reassigned_network_user_id() {
410 $backtrace = debug_backtrace( false ); // phpcs:ignore PHPCompatibility.PHP.NewFunctionParameters.debug_backtrace_optionsFound
411 foreach ( $backtrace as $call ) {
412 if (
413 'remove_user_from_blog' === $call['function'] &&
414 3 === count( $call['args'] )
415 ) {
416 return $call['args'][2];
417 }
418 }
419
420 return false;
421 }
422 }
423