User Switching - Version 1.4.0

Version Description

Download this release

Release Info

Developer johnbillion
Plugin Icon 128x128 User Switching
Version 1.4.0
Comparing to
See all releases

Code changes from version 1.3.1 to 1.4.0

Files changed (3) hide show
  1. composer.json +2 -2
  2. readme.txt +71 -15
  3. user-switching.php +305 -251
composer.json CHANGED
@@ -27,10 +27,10 @@
27
  "behat/mink-goutte-driver": "^1.1",
28
  "johnbillion/php-docs-standards": "^1.2",
29
  "johnbillion/wordpress-behat-extension": "^0.1.5",
30
- "johnpbloch/wordpress": ">=4.8.0@stable",
31
  "phpunit/phpunit": "^5",
32
  "wimg/php-compatibility": "^8",
33
- "wp-coding-standards/wpcs": "^0.13"
34
  },
35
  "extra": {
36
  "wordpress-install-dir": "vendor/wordpress"
27
  "behat/mink-goutte-driver": "^1.1",
28
  "johnbillion/php-docs-standards": "^1.2",
29
  "johnbillion/wordpress-behat-extension": "^0.1.5",
30
+ "johnpbloch/wordpress": ">=4.9.0@stable",
31
  "phpunit/phpunit": "^5",
32
  "wimg/php-compatibility": "^8",
33
+ "wp-coding-standards/wpcs": "1.0"
34
  },
35
  "extra": {
36
  "wordpress-install-dir": "vendor/wordpress"
readme.txt CHANGED
@@ -1,11 +1,12 @@
1
  === User Switching ===
2
 
3
- Contributors: johnbillion
4
- Tags: users, profiles, user switching, fast user switching, multisite, buddypress, bbpress, become, user management, developer
5
- Requires at least: 3.7
6
- Tested up to: 4.9
7
- Stable tag: 1.3.1
8
- License: GPL v2 or later
 
9
 
10
  Instant switching between user accounts in WordPress.
11
 
@@ -19,14 +20,15 @@ This plugin allows you to quickly swap between user accounts in WordPress at the
19
  * Switch back: Instantly switch back to your originating account.
20
  * Switch off: Log out of your account but retain the ability to instantly switch back in again.
21
  * It's completely secure (see the *Security* section below).
22
- * Compatible with WordPress, WordPress Multisite, BuddyPress and bbPress.
23
 
24
  = Security =
25
 
26
- * Only users with the ability to edit other users can switch user accounts. By default this is only Administrators on single site installs, and Super Admins on Multisite installs.
27
  * Passwords are not (and cannot be) revealed.
28
  * Uses the cookie authentication system in WordPress when remembering the account(s) you've switched from and when switching back.
29
  * Implements the nonce security system in WordPress, meaning only those who intend to switch users can switch.
 
30
  * Full support for administration over SSL (if applicable).
31
 
32
  = Usage =
@@ -39,7 +41,11 @@ See the [FAQ](https://wordpress.org/plugins/user-switching/faq/) for information
39
 
40
  = Privacy Statement =
41
 
42
- User Switching makes use of browser cookies in order to allow users to switch to another account. Its cookies operate using the same mechanism as the authentication cookies in WordPress core, therefore they contain the user's `user_login` field in plain text which should be treated as potentially personally identifiable information. The name of the cookies are `wordpress_user_sw_{hash}`, `wordpress_user_sw_secure_{hash}`, and `wordpress_user_sw_olduser_{hash}`, where `{hash}` is an identifier unique to the installation of WordPress.
 
 
 
 
43
 
44
  User Switching does not send data to any third party, nor does it include any third party resources, nor will it ever do so.
45
 
@@ -78,7 +84,13 @@ One exception I'm aware of is [Duo Security](https://wordpress.org/plugins/duo-w
78
 
79
  A user needs the `edit_users` capability in order to switch user accounts. By default only Administrators have this capability, and with Multisite enabled only Super Admins have this capability.
80
 
81
- = Can regular admins on Multisite installs switch accounts? =
 
 
 
 
 
 
82
 
83
  No. This can be enabled though by installing the [User Switching for Regular Admins](https://github.com/johnbillion/user-switching-for-regular-admins) plugin.
84
 
@@ -88,11 +100,55 @@ Yes, there's a third party add-on plugin for this: [Admin Bar User Switching](ht
88
 
89
  = Are any plugin actions called when a user switches account? =
90
 
91
- Yes. When a user switches to another account, the `switch_to_user` hook is called with the new and old user IDs passed as parameters.
92
-
93
- When a user switches back to their original account, the `switch_back_user` hook is called with the new (original) and old user IDs passed as parameters. Note that the old user ID can be boolean false if the user is switching back after they've been switched off.
94
-
95
- When a user switches off, the `switch_off_user` hook is called with the old user ID as a parameter.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  == Changelog ==
98
 
1
  === User Switching ===
2
 
3
+ Contributors: johnbillion
4
+ Tags: users, profiles, user switching, fast user switching, multisite, buddypress, bbpress, become, user management, developer
5
+ Requires at least: 3.7
6
+ Tested up to: 4.9
7
+ Stable tag: 1.4.0
8
+ Requires PHP: 5.3
9
+ License: GPL v2 or later
10
 
11
  Instant switching between user accounts in WordPress.
12
 
20
  * Switch back: Instantly switch back to your originating account.
21
  * Switch off: Log out of your account but retain the ability to instantly switch back in again.
22
  * It's completely secure (see the *Security* section below).
23
+ * Compatible with WordPress, WordPress Multisite, BuddyPress, and bbPress.
24
 
25
  = Security =
26
 
27
+ * Only users with the ability to edit other users can switch user accounts. By default this is only Administrators on single site installations, and Super Admins on Multisite installations.
28
  * Passwords are not (and cannot be) revealed.
29
  * Uses the cookie authentication system in WordPress when remembering the account(s) you've switched from and when switching back.
30
  * Implements the nonce security system in WordPress, meaning only those who intend to switch users can switch.
31
+ * Full support for user session validation where appropriate.
32
  * Full support for administration over SSL (if applicable).
33
 
34
  = Usage =
41
 
42
  = Privacy Statement =
43
 
44
+ User Switching makes use of browser cookies in order to allow users to switch to another account. Its cookies operate using the same mechanism as the authentication cookies in WordPress core, therefore their values contain the user's `user_login` field in plain text which should be treated as potentially personally identifiable information. The names of the cookies are:
45
+
46
+ * `wordpress_user_sw_{COOKIEHASH}`
47
+ * `wordpress_user_sw_secure_{COOKIEHASH}`
48
+ * `wordpress_user_sw_olduser_{COOKIEHASH}`
49
 
50
  User Switching does not send data to any third party, nor does it include any third party resources, nor will it ever do so.
51
 
84
 
85
  A user needs the `edit_users` capability in order to switch user accounts. By default only Administrators have this capability, and with Multisite enabled only Super Admins have this capability.
86
 
87
+ = Can the ability to switch accounts be granted to other users or roles? =
88
+
89
+ Yes. The `switch_users` meta capability can be explicitly granted to a user or a role to allow them to switch users regardless of whether or not they have the `edit_users` capability. For practical purposes, the user or role will also need the `list_users` capability so they can access the Users menu in the WordPress admin area.
90
+
91
+ If you know what you're doing with user capabilities, this capability can also be denied from a user or role to prevent the ability to switch users, regardless of whether or not they have the `edit_users` capability.
92
+
93
+ = Can regular admins on Multisite installations switch accounts? =
94
 
95
  No. This can be enabled though by installing the [User Switching for Regular Admins](https://github.com/johnbillion/user-switching-for-regular-admins) plugin.
96
 
100
 
101
  = Are any plugin actions called when a user switches account? =
102
 
103
+ Yes. When a user switches to another account, the `switch_to_user` hook is called:
104
+
105
+ /**
106
+ * Fires when a user switches to another user account.
107
+ *
108
+ * @since 0.6.0
109
+ * @since 1.4.0 The `$new_token` and `$old_token` parameters were added.
110
+ *
111
+ * @param int $user_id The ID of the user being switched to.
112
+ * @param int $old_user_id The ID of the user being switched from.
113
+ * @param string $new_token The token of the session of the user being switched to. Can be an empty string
114
+ * or a token for a session that may or may not still be valid.
115
+ * @param string $old_token The token of the session of the user being switched from.
116
+ */
117
+ do_action( 'switch_to_user', $user_id, $old_user_id, $new_token, $old_token );
118
+
119
+ When a user switches back to their originating account, the `switch_back_user` hook is called:
120
+
121
+ /**
122
+ * Fires when a user switches back to their originating account.
123
+ *
124
+ * @since 0.6.0
125
+ * @since 1.4.0 The `$new_token` and `$old_token` parameters were added.
126
+ *
127
+ * @param int $user_id The ID of the user being switched back to.
128
+ * @param int|false $old_user_id The ID of the user being switched from, or false if the user is switching back
129
+ * after having been switched off.
130
+ * @param string $new_token The token of the session of the user being switched to. Can be an empty string
131
+ * or a token for a session that may or may not still be valid.
132
+ * @param string $old_token The token of the session of the user being switched from.
133
+ */
134
+
135
+ When a user switches off, the `switch_off_user` hook is called:
136
+
137
+ /**
138
+ * Fires when a user switches off.
139
+ *
140
+ * @since 0.6.0
141
+ * @since 1.4.0 The `$old_token` parameter was added.
142
+ *
143
+ * @param int $old_user_id The ID of the user switching off.
144
+ * @param string $old_token The token of the session of the user switching off.
145
+ */
146
+
147
+ In addition, User Switching respects the following filters from WordPress core when appropriate:
148
+
149
+ * `login_redirect` when switching to another user.
150
+ * `logout_redirect` when switching off.
151
+ * `send_auth_cookies` before setting any cookies.
152
 
153
  == Changelog ==
154
 
user-switching.php CHANGED
@@ -8,15 +8,16 @@
8
  * @copyright 2009-2018 John Blackbourn
9
  * @license GPL v2 or later
10
  *
11
- * Plugin Name: User Switching
12
- * Description: Instant switching between user accounts in WordPress
13
- * Version: 1.3.1
14
- * Plugin URI: https://johnblackbourn.com/wordpress-plugin-user-switching/
15
- * Author: John Blackbourn & contributors
16
- * Author URI: https://github.com/johnbillion/user-switching/graphs/contributors
17
- * Text Domain: user-switching
18
- * Domain Path: /languages/
19
- * Network: true
 
20
  *
21
  * This program is free software; you can redistribute it and/or modify
22
  * it under the terms of the GNU General Public License as published by
@@ -35,17 +36,16 @@
35
  class user_switching {
36
 
37
  /**
38
- * The name used to identify the application for debugging purposes.
39
  *
40
  * @var string
41
  */
42
  public static $application = 'WordPress/User Switching';
43
 
44
  /**
45
- * Class constructor. Sets up some filters and actions.
46
  */
47
- private function __construct() {
48
-
49
  // Required functionality:
50
  add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 4 );
51
  add_filter( 'map_meta_cap', array( $this, 'filter_map_meta_cap' ), 10, 4 );
@@ -67,14 +67,12 @@ class user_switching {
67
  add_action( 'bp_member_header_actions', array( $this, 'action_bp_button' ), 11 );
68
  add_action( 'bp_directory_members_actions', array( $this, 'action_bp_button' ), 11 );
69
  add_action( 'bbp_template_after_user_details', array( $this, 'action_bbpress_button' ) );
70
-
71
  }
72
 
73
  /**
74
- * Defines the names of our cookies.
75
  */
76
  public function action_plugins_loaded() {
77
-
78
  // User Switching's auth_cookie
79
  if ( ! defined( 'USER_SWITCHING_COOKIE' ) ) {
80
  define( 'USER_SWITCHING_COOKIE', 'wordpress_user_sw_' . COOKIEHASH );
@@ -89,11 +87,10 @@ class user_switching {
89
  if ( ! defined( 'USER_SWITCHING_OLDUSER_COOKIE' ) ) {
90
  define( 'USER_SWITCHING_OLDUSER_COOKIE', 'wordpress_user_sw_olduser_' . COOKIEHASH );
91
  }
92
-
93
  }
94
 
95
  /**
96
- * Outputs the 'Switch To' link on the user editing screen if we have permission to switch to this user.
97
  *
98
  * @param WP_User $user User object for this screen.
99
  */
@@ -120,32 +117,19 @@ class user_switching {
120
  * @return bool Whether the current user is being 'remembered' or not.
121
  */
122
  public static function remember() {
123
-
124
- /**
125
- * Filter the duration of the authentication cookie expiration period.
126
- *
127
- * This matches the WordPress core filter in `wp_set_auth_cookie()`.
128
- *
129
- * @since 0.2.2
130
- *
131
- * @param int $length Duration of the expiration period in seconds.
132
- * @param int $user_id User ID.
133
- * @param bool $remember Whether to remember the user login. Default false.
134
- */
135
  $cookie_life = apply_filters( 'auth_cookie_expiration', 172800, get_current_user_id(), false );
136
  $current = wp_parse_auth_cookie( '', 'logged_in' );
137
 
138
  // Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
139
  // If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
140
  return ( ( $current['expiration'] - time() ) > $cookie_life );
141
-
142
  }
143
 
144
  /**
145
  * Loads localisation files and routes actions depending on the 'action' query var.
146
  */
147
  public function action_init() {
148
-
149
  load_plugin_textdomain( 'user-switching', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
150
 
151
  if ( ! isset( $_REQUEST['action'] ) ) {
@@ -175,7 +159,6 @@ class user_switching {
175
  // Switch user:
176
  $user = switch_to_user( $user_id, self::remember() );
177
  if ( $user ) {
178
-
179
  $redirect_to = self::get_redirect( $user, $current_user );
180
 
181
  // Redirect to the dashboard or the home URL depending on capabilities:
@@ -191,7 +174,6 @@ class user_switching {
191
  wp_safe_redirect( add_query_arg( $args, admin_url() ), 302, self::$application );
192
  }
193
  exit;
194
-
195
  } else {
196
  wp_die( esc_html__( 'Could not switch users.', 'user-switching' ) );
197
  }
@@ -217,13 +199,13 @@ class user_switching {
217
  if ( switch_to_user( $old_user->ID, self::remember(), false ) ) {
218
 
219
  if ( ! empty( $_REQUEST['interim-login'] ) ) {
220
- $GLOBALS['interim_login'] = 'success';
221
  login_header( '', '' );
222
  exit;
223
  }
224
 
225
  $redirect_to = self::get_redirect( $old_user, $current_user );
226
- $args = array(
227
  'user_switched' => 'true',
228
  'switched_back' => 'true',
229
  );
@@ -253,7 +235,7 @@ class user_switching {
253
  // Switch off:
254
  if ( switch_off_user() ) {
255
  $redirect_to = self::get_redirect( null, $current_user );
256
- $args = array(
257
  'switched_off' => 'true',
258
  );
259
 
@@ -270,7 +252,6 @@ class user_switching {
270
  break;
271
 
272
  }
273
-
274
  }
275
 
276
  /**
@@ -281,45 +262,23 @@ class user_switching {
281
  * @return string The URL to redirect to.
282
  */
283
  protected static function get_redirect( WP_User $new_user = null, WP_User $old_user = null ) {
284
-
285
  if ( ! empty( $_REQUEST['redirect_to'] ) ) {
286
- $redirect_to = self::remove_query_args( wp_unslash( $_REQUEST['redirect_to'] ) ); // WPCS: sanitization ok
287
  $requested_redirect_to = wp_unslash( $_REQUEST['redirect_to'] ); // WPCS: sanitization ok
288
  } else {
289
- $redirect_to = '';
290
  $requested_redirect_to = '';
291
  }
292
 
293
  if ( ! $new_user ) {
294
- /**
295
- * Filter the redirect URL when a user switches off.
296
- *
297
- * This matches the WordPress core filter in wp-login.php.
298
- *
299
- * @since 1.0.4
300
- *
301
- * @param string $redirect_to The redirect destination URL.
302
- * @param string $requested_redirect_to The requested redirect destination URL passed as a parameter.
303
- * @param WP_User $old_user The WP_User object for the user that's switching off.
304
- */
305
  $redirect_to = apply_filters( 'logout_redirect', $redirect_to, $requested_redirect_to, $old_user );
306
  } else {
307
- /**
308
- * Filter the redirect URL when a user switches to another user or switches back.
309
- *
310
- * This matches the WordPress core filter in wp-login.php.
311
- *
312
- * @since 0.8.7
313
- *
314
- * @param string $redirect_to The redirect destination URL.
315
- * @param string $requested_redirect_to The requested redirect destination URL passed as a parameter.
316
- * @param WP_User $new_user The WP_User object for the user that's being switched to.
317
- */
318
  $redirect_to = apply_filters( 'login_redirect', $redirect_to, $requested_redirect_to, $new_user );
319
  }
320
 
321
  return $redirect_to;
322
-
323
  }
324
 
325
  /**
@@ -330,12 +289,11 @@ class user_switching {
330
  $old_user = self::get_old_user();
331
 
332
  if ( $old_user ) {
333
-
334
  ?>
335
  <div id="user_switching" class="updated notice is-dismissible">
336
- <p><span class="dashicons dashicons-admin-users" style="color:#56c234"></span>
337
  <?php
338
- $message = '';
339
  $just_switched = isset( $_GET['user_switched'] );
340
  if ( $just_switched ) {
341
  $message = esc_html( sprintf(
@@ -348,6 +306,7 @@ class user_switching {
348
  $switch_back_url = add_query_arg( array(
349
  'redirect_to' => urlencode( self::current_url() ),
350
  ), self::switch_back_url( $old_user ) );
 
351
  $message .= sprintf(
352
  ' <a href="%s">%s</a>.',
353
  esc_url( $switch_back_url ),
@@ -376,9 +335,7 @@ class user_switching {
376
  </p>
377
  </div>
378
  <?php
379
-
380
  } elseif ( isset( $_GET['user_switched'] ) ) {
381
-
382
  ?>
383
  <div id="user_switching" class="updated notice is-dismissible">
384
  <p>
@@ -402,14 +359,13 @@ class user_switching {
402
  </p>
403
  </div>
404
  <?php
405
-
406
  }
407
  }
408
 
409
  /**
410
  * Validates the old user cookie and returns its user data.
411
  *
412
- * @return bool|WP_User False if there's no old user cookie or it's invalid, WP_User object if it's present and valid.
413
  */
414
  public static function get_old_user() {
415
  $cookie = user_switching_get_olduser_cookie();
@@ -426,14 +382,13 @@ class user_switching {
426
  /**
427
  * Authenticates an old user by verifying the latest entry in the auth cookie.
428
  *
429
- * @param WP_User $user A WP_User object (usually from the logged_in cookie).
430
  * @return bool Whether verification with the auth cookie passed.
431
  */
432
  public static function authenticate_old_user( WP_User $user ) {
433
  $cookie = user_switching_get_auth_cookie();
434
  if ( ! empty( $cookie ) ) {
435
-
436
- if ( user_switching::secure_auth_cookie() ) {
437
  $scheme = 'secure_auth';
438
  } else {
439
  $scheme = 'auth';
@@ -454,7 +409,6 @@ class user_switching {
454
  * @param WP_Admin_Bar $wp_admin_bar The admin bar object.
455
  */
456
  public function action_admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) {
457
-
458
  if ( ! function_exists( 'is_admin_bar_showing' ) ) {
459
  return;
460
  }
@@ -477,7 +431,6 @@ class user_switching {
477
  $old_user = self::get_old_user();
478
 
479
  if ( $old_user ) {
480
-
481
  $wp_admin_bar->add_menu( array(
482
  'parent' => $parent,
483
  'id' => 'switch-back',
@@ -491,11 +444,9 @@ class user_switching {
491
  'redirect_to' => urlencode( self::current_url() ),
492
  ), self::switch_back_url( $old_user ) ),
493
  ) );
494
-
495
  }
496
 
497
  if ( current_user_can( 'switch_off' ) ) {
498
-
499
  $url = self::switch_off_url( wp_get_current_user() );
500
  if ( ! is_admin() ) {
501
  $url = add_query_arg( array(
@@ -510,9 +461,7 @@ class user_switching {
510
  'title' => esc_html__( 'Switch Off', 'user-switching' ),
511
  'href' => $url,
512
  ) );
513
-
514
  }
515
-
516
  }
517
 
518
  /**
@@ -533,7 +482,6 @@ class user_switching {
533
  ), self::switch_back_url( $old_user ) );
534
  echo '<li id="user_switching_switch_on"><a href="' . esc_url( $url ) . '">' . esc_html( $link ) . '</a></li>';
535
  }
536
-
537
  }
538
 
539
  /**
@@ -558,7 +506,6 @@ class user_switching {
558
  ), self::switch_back_url( $old_user ) );
559
  echo '<p id="user_switching_switch_on"><a href="' . esc_url( $url ) . '">' . esc_html( $link ) . '</a></p>';
560
  }
561
-
562
  }
563
 
564
  /**
@@ -590,13 +537,12 @@ class user_switching {
590
  }
591
 
592
  $message .= '<p class="message" id="user_switching_switch_on">';
593
- $message .= '<span class="dashicons dashicons-admin-users" style="color:#56c234"></span> ';
594
  $message .= '<a href="' . esc_url( $url ) . '" onclick="window.location.href=\'' . esc_url( $url ) . '\';return false;">' . esc_html( $link ) . '</a>';
595
  $message .= '</p>';
596
  }
597
 
598
  return $message;
599
-
600
  }
601
 
602
  /**
@@ -653,7 +599,6 @@ class user_switching {
653
  'link_text' => esc_html__( 'Switch&nbsp;To', 'user-switching' ),
654
  'wrapper_id' => 'user_switching_switch_to',
655
  ) );
656
-
657
  }
658
 
659
  /**
@@ -681,7 +626,6 @@ class user_switching {
681
  <li><a href="<?php echo esc_url( $link ); ?>"><?php esc_html_e( 'Switch&nbsp;To', 'user-switching' ); ?></a></li>
682
  </ul>
683
  <?php
684
-
685
  }
686
 
687
  /**
@@ -704,10 +648,9 @@ class user_switching {
704
  * Returns the switch to or switch back URL for a given user.
705
  *
706
  * @param WP_User $user The user to be switched to.
707
- * @return string|bool The required URL, or false if there's no old user or the user doesn't have the required capability.
708
  */
709
  public static function maybe_switch_url( WP_User $user ) {
710
-
711
  $old_user = self::get_old_user();
712
 
713
  if ( $old_user && ( $old_user->ID === $user->ID ) ) {
@@ -717,7 +660,6 @@ class user_switching {
717
  } else {
718
  return false;
719
  }
720
-
721
  }
722
 
723
  /**
@@ -817,7 +759,8 @@ class user_switching {
817
  *
818
  * Important: This does not get called for Super Admins. See filter_map_meta_cap() below.
819
  *
820
- * @param bool[] $user_caps Concerned user's capabilities.
 
821
  * @param string[] $required_caps Required primitive capabilities for the requested capability.
822
  * @param array $args {
823
  * Arguments that accompany the requested capability check.
@@ -831,10 +774,21 @@ class user_switching {
831
  */
832
  public function filter_user_has_cap( array $user_caps, array $required_caps, array $args, WP_User $user ) {
833
  if ( 'switch_to_user' === $args[0] ) {
 
 
 
 
 
834
  $user_caps['switch_to_user'] = ( user_can( $user->ID, 'edit_user', $args[2] ) && ( $args[2] !== $user->ID ) );
835
  } elseif ( 'switch_off' === $args[0] ) {
 
 
 
 
 
836
  $user_caps['switch_off'] = user_can( $user->ID, 'edit_users' );
837
  }
 
838
  return $user_caps;
839
  }
840
 
@@ -873,217 +827,317 @@ class user_switching {
873
  static $instance;
874
 
875
  if ( ! isset( $instance ) ) {
876
- $instance = new user_switching;
877
  }
878
 
879
  return $instance;
880
  }
881
 
 
 
 
 
 
882
  }
883
 
884
  if ( ! function_exists( 'user_switching_set_olduser_cookie' ) ) {
885
- /**
886
- * Sets authorisation cookies containing the originating user information.
887
- *
888
- * @param int $old_user_id The ID of the originating user, usually the current logged in user.
889
- * @param bool $pop Optional. Pop the latest user off the auth cookie, instead of appending the new one. Default false.
890
- */
891
- function user_switching_set_olduser_cookie( $old_user_id, $pop = false ) {
892
- $secure_auth_cookie = user_switching::secure_auth_cookie();
893
- $secure_olduser_cookie = user_switching::secure_olduser_cookie();
894
- $expiration = time() + 172800; // 48 hours
895
- $auth_cookie = user_switching_get_auth_cookie();
896
- $olduser_cookie = wp_generate_auth_cookie( $old_user_id, $expiration, 'logged_in' );
897
-
898
- if ( $secure_auth_cookie ) {
899
- $auth_cookie_name = USER_SWITCHING_SECURE_COOKIE;
900
- $scheme = 'secure_auth';
901
- } else {
902
- $auth_cookie_name = USER_SWITCHING_COOKIE;
903
- $scheme = 'auth';
904
- }
 
 
 
905
 
906
- if ( $pop ) {
907
- array_pop( $auth_cookie );
908
- } else {
909
- array_push( $auth_cookie, wp_generate_auth_cookie( $old_user_id, $expiration, $scheme ) );
910
- }
911
 
912
- setcookie( $auth_cookie_name, json_encode( $auth_cookie ), $expiration, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_auth_cookie, true );
913
- setcookie( USER_SWITCHING_OLDUSER_COOKIE, $olduser_cookie, $expiration, COOKIEPATH, COOKIE_DOMAIN, $secure_olduser_cookie, true );
914
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
915
  }
916
 
917
  if ( ! function_exists( 'user_switching_clear_olduser_cookie' ) ) {
918
- /**
919
- * Clears the cookies containing the originating user, or pops the latest item off the end if there's more than one.
920
- *
921
- * @param bool $clear_all Optional. Whether to clear the cookies (as opposed to just popping the last user off the end). Default true.
922
- */
923
- function user_switching_clear_olduser_cookie( $clear_all = true ) {
924
- $auth_cookie = user_switching_get_auth_cookie();
925
- if ( ! empty( $auth_cookie ) ) {
926
- array_pop( $auth_cookie );
927
- }
928
- if ( $clear_all || empty( $auth_cookie ) ) {
929
- $expire = time() - 31536000;
930
- setcookie( USER_SWITCHING_COOKIE, ' ', $expire, SITECOOKIEPATH, COOKIE_DOMAIN );
931
- setcookie( USER_SWITCHING_SECURE_COOKIE, ' ', $expire, SITECOOKIEPATH, COOKIE_DOMAIN );
932
- setcookie( USER_SWITCHING_OLDUSER_COOKIE, ' ', $expire, COOKIEPATH, COOKIE_DOMAIN );
933
- } else {
 
934
 
935
- if ( user_switching::secure_auth_cookie() ) {
936
- $scheme = 'secure_auth';
 
 
 
 
 
 
 
937
  } else {
938
- $scheme = 'auth';
939
- }
 
 
 
940
 
941
- $old_user_id = wp_validate_auth_cookie( end( $auth_cookie ), $scheme );
942
- if ( $old_user_id ) {
943
- user_switching_set_olduser_cookie( $old_user_id, true );
 
 
 
 
944
  }
945
  }
946
  }
947
- }
948
 
949
  if ( ! function_exists( 'user_switching_get_olduser_cookie' ) ) {
950
- /**
951
- * Gets the value of the cookie containing the originating user.
952
- *
953
- * @return string|bool The old user cookie, or boolean false if there isn't one.
954
- */
955
- function user_switching_get_olduser_cookie() {
956
- if ( isset( $_COOKIE[ USER_SWITCHING_OLDUSER_COOKIE ] ) ) {
957
- return wp_unslash( $_COOKIE[ USER_SWITCHING_OLDUSER_COOKIE ] ); // WPCS: sanitization ok
958
- } else {
959
- return false;
 
960
  }
961
  }
962
- }
963
 
964
  if ( ! function_exists( 'user_switching_get_auth_cookie' ) ) {
965
- /**
966
- * Gets the value of the auth cookie containing the list of originating users.
967
- *
968
- * @return string[] Array of originating user authentication cookie values. Empty array if there are none.
969
- */
970
- function user_switching_get_auth_cookie() {
971
- if ( user_switching::secure_auth_cookie() ) {
972
- $auth_cookie_name = USER_SWITCHING_SECURE_COOKIE;
973
- } else {
974
- $auth_cookie_name = USER_SWITCHING_COOKIE;
975
- }
976
 
977
- if ( isset( $_COOKIE[ $auth_cookie_name ] ) ) {
978
- $cookie = json_decode( wp_unslash( $_COOKIE[ $auth_cookie_name ] ) ); // WPCS: sanitization ok
979
- }
980
- if ( ! isset( $cookie ) || ! is_array( $cookie ) ) {
981
- $cookie = array();
 
 
982
  }
983
- return $cookie;
984
- }
985
  }
986
 
987
  if ( ! function_exists( 'switch_to_user' ) ) {
988
- /**
989
- * Switches the current logged in user to the specified user.
990
- *
991
- * @param int $user_id The ID of the user to switch to.
992
- * @param bool $remember Optional. Whether to 'remember' the user in the form of a persistent browser cookie. Default false.
993
- * @param bool $set_old_user Optional. Whether to set the old user cookie. Default true.
994
- * @return bool|WP_User WP_User object on success, false on failure.
995
- */
996
- function switch_to_user( $user_id, $remember = false, $set_old_user = true ) {
997
- $user = get_userdata( $user_id );
998
-
999
- if ( ! $user ) {
1000
- return false;
1001
- }
1002
 
1003
- $old_user_id = ( is_user_logged_in() ) ? get_current_user_id() : false;
 
 
1004
 
1005
- if ( $set_old_user && $old_user_id ) {
1006
- user_switching_set_olduser_cookie( $old_user_id );
1007
- } else {
1008
- user_switching_clear_olduser_cookie( false );
1009
- }
1010
 
1011
- wp_clear_auth_cookie();
1012
- wp_set_auth_cookie( $user_id, $remember );
1013
- wp_set_current_user( $user_id );
 
 
 
 
 
 
1014
 
1015
- if ( $set_old_user ) {
1016
  /**
1017
- * Fires when a user switches to another user account.
1018
- *
1019
- * @since 0.6.0
1020
- *
1021
- * @param int $user_id The ID of the user being switched to.
1022
- * @param int $old_user_id The ID of the user being switched from.
1023
- */
1024
- do_action( 'switch_to_user', $user_id, $old_user_id );
1025
- } else {
1026
- /**
1027
- * Fires when a user switches back to their originating account.
1028
- *
1029
- * @since 0.6.0
1030
  *
1031
- * @param int $user_id The ID of the user being switched back to.
1032
- * @param int|false $old_user_id The ID of the user being switched from, or false if the user is switching back
1033
- * after having been switched off.
1034
  */
1035
- do_action( 'switch_back_user', $user_id, $old_user_id );
1036
- }
 
 
 
1037
 
1038
- return $user;
1039
- }
1040
- }
1041
 
1042
- if ( ! function_exists( 'switch_off_user' ) ) {
1043
- /**
1044
- * Switches off the current logged in user. This logs the current user out while retaining a cookie allowing them to log
1045
- * straight back in using the 'Switch back to {user}' system.
1046
- *
1047
- * @return bool True on success, false on failure.
1048
- */
1049
- function switch_off_user() {
1050
- $old_user_id = get_current_user_id();
1051
 
1052
- if ( ! $old_user_id ) {
1053
- return false;
1054
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1055
 
1056
- user_switching_set_olduser_cookie( $old_user_id );
1057
- wp_clear_auth_cookie();
1058
- wp_set_current_user( 0 );
1059
 
 
1060
  /**
1061
- * Fires when a user switches off.
 
1062
  *
1063
- * @since 0.6.0
1064
- *
1065
- * @param int $old_user_id The ID of the user switching off.
1066
  */
1067
- do_action( 'switch_off_user', $old_user_id );
 
1068
 
1069
- return true;
1070
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1071
  }
1072
 
1073
  if ( ! function_exists( 'current_user_switched' ) ) {
1074
- /**
1075
- * Returns whether or not the current user switched into their account.
1076
- *
1077
- * @return bool|WP_User False if the user isn't logged in or they didn't switch in; old user object (which evaluates to
1078
- * true) if the user switched into the current user account.
1079
- */
1080
- function current_user_switched() {
1081
- if ( ! is_user_logged_in() ) {
1082
- return false;
1083
- }
1084
 
1085
- return user_switching::get_old_user();
1086
- }
1087
  }
1088
 
1089
  $GLOBALS['user_switching'] = user_switching::get_instance();
 
8
  * @copyright 2009-2018 John Blackbourn
9
  * @license GPL v2 or later
10
  *
11
+ * Plugin Name: User Switching
12
+ * Description: Instant switching between user accounts in WordPress
13
+ * Version: 1.4.0
14
+ * Plugin URI: https://johnblackbourn.com/wordpress-plugin-user-switching/
15
+ * Author: John Blackbourn & contributors
16
+ * Author URI: https://github.com/johnbillion/user-switching/graphs/contributors
17
+ * Text Domain: user-switching
18
+ * Domain Path: /languages/
19
+ * Network: true
20
+ * Requires PHP: 5.3
21
  *
22
  * This program is free software; you can redistribute it and/or modify
23
  * it under the terms of the GNU General Public License as published by
36
  class user_switching {
37
 
38
  /**
39
+ * The name used to identify the application during a WordPress redirect.
40
  *
41
  * @var string
42
  */
43
  public static $application = 'WordPress/User Switching';
44
 
45
  /**
46
+ * Sets up all the filters and actions.
47
  */
48
+ public function init_hooks() {
 
49
  // Required functionality:
50
  add_filter( 'user_has_cap', array( $this, 'filter_user_has_cap' ), 10, 4 );
51
  add_filter( 'map_meta_cap', array( $this, 'filter_map_meta_cap' ), 10, 4 );
67
  add_action( 'bp_member_header_actions', array( $this, 'action_bp_button' ), 11 );
68
  add_action( 'bp_directory_members_actions', array( $this, 'action_bp_button' ), 11 );
69
  add_action( 'bbp_template_after_user_details', array( $this, 'action_bbpress_button' ) );
 
70
  }
71
 
72
  /**
73
+ * Defines the names of the cookies used by User Switching.
74
  */
75
  public function action_plugins_loaded() {
 
76
  // User Switching's auth_cookie
77
  if ( ! defined( 'USER_SWITCHING_COOKIE' ) ) {
78
  define( 'USER_SWITCHING_COOKIE', 'wordpress_user_sw_' . COOKIEHASH );
87
  if ( ! defined( 'USER_SWITCHING_OLDUSER_COOKIE' ) ) {
88
  define( 'USER_SWITCHING_OLDUSER_COOKIE', 'wordpress_user_sw_olduser_' . COOKIEHASH );
89
  }
 
90
  }
91
 
92
  /**
93
+ * Outputs the 'Switch To' link on the user editing screen if the current user has permission to switch to them.
94
  *
95
  * @param WP_User $user User object for this screen.
96
  */
117
  * @return bool Whether the current user is being 'remembered' or not.
118
  */
119
  public static function remember() {
120
+ /** This filter is documented in wp-includes/pluggable.php */
 
 
 
 
 
 
 
 
 
 
 
121
  $cookie_life = apply_filters( 'auth_cookie_expiration', 172800, get_current_user_id(), false );
122
  $current = wp_parse_auth_cookie( '', 'logged_in' );
123
 
124
  // Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
125
  // If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
126
  return ( ( $current['expiration'] - time() ) > $cookie_life );
 
127
  }
128
 
129
  /**
130
  * Loads localisation files and routes actions depending on the 'action' query var.
131
  */
132
  public function action_init() {
 
133
  load_plugin_textdomain( 'user-switching', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
134
 
135
  if ( ! isset( $_REQUEST['action'] ) ) {
159
  // Switch user:
160
  $user = switch_to_user( $user_id, self::remember() );
161
  if ( $user ) {
 
162
  $redirect_to = self::get_redirect( $user, $current_user );
163
 
164
  // Redirect to the dashboard or the home URL depending on capabilities:
174
  wp_safe_redirect( add_query_arg( $args, admin_url() ), 302, self::$application );
175
  }
176
  exit;
 
177
  } else {
178
  wp_die( esc_html__( 'Could not switch users.', 'user-switching' ) );
179
  }
199
  if ( switch_to_user( $old_user->ID, self::remember(), false ) ) {
200
 
201
  if ( ! empty( $_REQUEST['interim-login'] ) ) {
202
+ $GLOBALS['interim_login'] = 'success'; // @codingStandardsIgnoreLine
203
  login_header( '', '' );
204
  exit;
205
  }
206
 
207
  $redirect_to = self::get_redirect( $old_user, $current_user );
208
+ $args = array(
209
  'user_switched' => 'true',
210
  'switched_back' => 'true',
211
  );
235
  // Switch off:
236
  if ( switch_off_user() ) {
237
  $redirect_to = self::get_redirect( null, $current_user );
238
+ $args = array(
239
  'switched_off' => 'true',
240
  );
241
 
252
  break;
253
 
254
  }
 
255
  }
256
 
257
  /**
262
  * @return string The URL to redirect to.
263
  */
264
  protected static function get_redirect( WP_User $new_user = null, WP_User $old_user = null ) {
 
265
  if ( ! empty( $_REQUEST['redirect_to'] ) ) {
266
+ $redirect_to = self::remove_query_args( wp_unslash( $_REQUEST['redirect_to'] ) ); // WPCS: sanitization ok
267
  $requested_redirect_to = wp_unslash( $_REQUEST['redirect_to'] ); // WPCS: sanitization ok
268
  } else {
269
+ $redirect_to = '';
270
  $requested_redirect_to = '';
271
  }
272
 
273
  if ( ! $new_user ) {
274
+ /** This filter is documented in wp-login.php */
 
 
 
 
 
 
 
 
 
 
275
  $redirect_to = apply_filters( 'logout_redirect', $redirect_to, $requested_redirect_to, $old_user );
276
  } else {
277
+ /** This filter is documented in wp-login.php */
 
 
 
 
 
 
 
 
 
 
278
  $redirect_to = apply_filters( 'login_redirect', $redirect_to, $requested_redirect_to, $new_user );
279
  }
280
 
281
  return $redirect_to;
 
282
  }
283
 
284
  /**
289
  $old_user = self::get_old_user();
290
 
291
  if ( $old_user ) {
 
292
  ?>
293
  <div id="user_switching" class="updated notice is-dismissible">
294
+ <p><span class="dashicons dashicons-admin-users" style="color:#56c234" aria-hidden="true"></span>
295
  <?php
296
+ $message = '';
297
  $just_switched = isset( $_GET['user_switched'] );
298
  if ( $just_switched ) {
299
  $message = esc_html( sprintf(
306
  $switch_back_url = add_query_arg( array(
307
  'redirect_to' => urlencode( self::current_url() ),
308
  ), self::switch_back_url( $old_user ) );
309
+
310
  $message .= sprintf(
311
  ' <a href="%s">%s</a>.',
312
  esc_url( $switch_back_url ),
335
  </p>
336
  </div>
337
  <?php
 
338
  } elseif ( isset( $_GET['user_switched'] ) ) {
 
339
  ?>
340
  <div id="user_switching" class="updated notice is-dismissible">
341
  <p>
359
  </p>
360
  </div>
361
  <?php
 
362
  }
363
  }
364
 
365
  /**
366
  * Validates the old user cookie and returns its user data.
367
  *
368
+ * @return false|WP_User False if there's no old user cookie or it's invalid, WP_User object if it's present and valid.
369
  */
370
  public static function get_old_user() {
371
  $cookie = user_switching_get_olduser_cookie();
382
  /**
383
  * Authenticates an old user by verifying the latest entry in the auth cookie.
384
  *
385
+ * @param WP_User $user A WP_User object (usually from the logged_in cookie).
386
  * @return bool Whether verification with the auth cookie passed.
387
  */
388
  public static function authenticate_old_user( WP_User $user ) {
389
  $cookie = user_switching_get_auth_cookie();
390
  if ( ! empty( $cookie ) ) {
391
+ if ( self::secure_auth_cookie() ) {
 
392
  $scheme = 'secure_auth';
393
  } else {
394
  $scheme = 'auth';
409
  * @param WP_Admin_Bar $wp_admin_bar The admin bar object.
410
  */
411
  public function action_admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) {
 
412
  if ( ! function_exists( 'is_admin_bar_showing' ) ) {
413
  return;
414
  }
431
  $old_user = self::get_old_user();
432
 
433
  if ( $old_user ) {
 
434
  $wp_admin_bar->add_menu( array(
435
  'parent' => $parent,
436
  'id' => 'switch-back',
444
  'redirect_to' => urlencode( self::current_url() ),
445
  ), self::switch_back_url( $old_user ) ),
446
  ) );
 
447
  }
448
 
449
  if ( current_user_can( 'switch_off' ) ) {
 
450
  $url = self::switch_off_url( wp_get_current_user() );
451
  if ( ! is_admin() ) {
452
  $url = add_query_arg( array(
461
  'title' => esc_html__( 'Switch Off', 'user-switching' ),
462
  'href' => $url,
463
  ) );
 
464
  }
 
465
  }
466
 
467
  /**
482
  ), self::switch_back_url( $old_user ) );
483
  echo '<li id="user_switching_switch_on"><a href="' . esc_url( $url ) . '">' . esc_html( $link ) . '</a></li>';
484
  }
 
485
  }
486
 
487
  /**
506
  ), self::switch_back_url( $old_user ) );
507
  echo '<p id="user_switching_switch_on"><a href="' . esc_url( $url ) . '">' . esc_html( $link ) . '</a></p>';
508
  }
 
509
  }
510
 
511
  /**
537
  }
538
 
539
  $message .= '<p class="message" id="user_switching_switch_on">';
540
+ $message .= '<span class="dashicons dashicons-admin-users" style="color:#56c234" aria-hidden="true"></span> ';
541
  $message .= '<a href="' . esc_url( $url ) . '" onclick="window.location.href=\'' . esc_url( $url ) . '\';return false;">' . esc_html( $link ) . '</a>';
542
  $message .= '</p>';
543
  }
544
 
545
  return $message;
 
546
  }
547
 
548
  /**
599
  'link_text' => esc_html__( 'Switch&nbsp;To', 'user-switching' ),
600
  'wrapper_id' => 'user_switching_switch_to',
601
  ) );
 
602
  }
603
 
604
  /**
626
  <li><a href="<?php echo esc_url( $link ); ?>"><?php esc_html_e( 'Switch&nbsp;To', 'user-switching' ); ?></a></li>
627
  </ul>
628
  <?php
 
629
  }
630
 
631
  /**
648
  * Returns the switch to or switch back URL for a given user.
649
  *
650
  * @param WP_User $user The user to be switched to.
651
+ * @return string|false The required URL, or false if there's no old user or the user doesn't have the required capability.
652
  */
653
  public static function maybe_switch_url( WP_User $user ) {
 
654
  $old_user = self::get_old_user();
655
 
656
  if ( $old_user && ( $old_user->ID === $user->ID ) ) {
660
  } else {
661
  return false;
662
  }
 
663
  }
664
 
665
  /**
759
  *
760
  * Important: This does not get called for Super Admins. See filter_map_meta_cap() below.
761
  *
762
+ * @param bool[] $user_caps Array of key/value pairs where keys represent a capability name and boolean values
763
+ * represent whether the user has that capability.
764
  * @param string[] $required_caps Required primitive capabilities for the requested capability.
765
  * @param array $args {
766
  * Arguments that accompany the requested capability check.
774
  */
775
  public function filter_user_has_cap( array $user_caps, array $required_caps, array $args, WP_User $user ) {
776
  if ( 'switch_to_user' === $args[0] ) {
777
+ if ( array_key_exists( 'switch_users', $user_caps ) ) {
778
+ $user_caps['switch_to_user'] = $user_caps['switch_users'];
779
+ return $user_caps;
780
+ }
781
+
782
  $user_caps['switch_to_user'] = ( user_can( $user->ID, 'edit_user', $args[2] ) && ( $args[2] !== $user->ID ) );
783
  } elseif ( 'switch_off' === $args[0] ) {
784
+ if ( array_key_exists( 'switch_users', $user_caps ) ) {
785
+ $user_caps['switch_off'] = $user_caps['switch_users'];
786
+ return $user_caps;
787
+ }
788
+
789
  $user_caps['switch_off'] = user_can( $user->ID, 'edit_users' );
790
  }
791
+
792
  return $user_caps;
793
  }
794
 
827
  static $instance;
828
 
829
  if ( ! isset( $instance ) ) {
830
+ $instance = new user_switching();
831
  }
832
 
833
  return $instance;
834
  }
835
 
836
+ /**
837
+ * Private class constructor. Use `get_instance()` to get the instance.
838
+ */
839
+ final private function __construct() {}
840
+
841
  }
842
 
843
  if ( ! function_exists( 'user_switching_set_olduser_cookie' ) ) {
844
+ /**
845
+ * Sets authorisation cookies containing the originating user information.
846
+ *
847
+ * @since 1.4.0 The `$token` parameter was added.
848
+ *
849
+ * @param int $old_user_id The ID of the originating user, usually the current logged in user.
850
+ * @param bool $pop Optional. Pop the latest user off the auth cookie, instead of appending the new one. Default false.
851
+ * @param string $token Optional. The old user's session token to store for later reuse. Default empty string.
852
+ */
853
+ function user_switching_set_olduser_cookie( $old_user_id, $pop = false, $token = '' ) {
854
+ $secure_auth_cookie = user_switching::secure_auth_cookie();
855
+ $secure_olduser_cookie = user_switching::secure_olduser_cookie();
856
+ $expiration = time() + 172800; // 48 hours
857
+ $auth_cookie = user_switching_get_auth_cookie();
858
+ $olduser_cookie = wp_generate_auth_cookie( $old_user_id, $expiration, 'logged_in', $token );
859
+
860
+ if ( $secure_auth_cookie ) {
861
+ $auth_cookie_name = USER_SWITCHING_SECURE_COOKIE;
862
+ $scheme = 'secure_auth';
863
+ } else {
864
+ $auth_cookie_name = USER_SWITCHING_COOKIE;
865
+ $scheme = 'auth';
866
+ }
867
 
868
+ if ( $pop ) {
869
+ array_pop( $auth_cookie );
870
+ } else {
871
+ array_push( $auth_cookie, wp_generate_auth_cookie( $old_user_id, $expiration, $scheme, $token ) );
872
+ }
873
 
874
+ $auth_cookie = json_encode( $auth_cookie );
875
+
876
+ /**
877
+ * Fires immediately before the User Switching authentication cookie is set.
878
+ *
879
+ * @since 1.4.0
880
+ *
881
+ * @param string $auth_cookie JSON-encoded array of authentication cookie values.
882
+ * @param int $expiration The time when the authentication cookie expires as a UNIX timestamp.
883
+ * Default is 48 hours from now.
884
+ * @param int $old_user_id User ID.
885
+ * @param string $scheme Authentication scheme. Values include 'auth' or 'secure_auth'.
886
+ * @param string $token User's session token to use for the latest cookie.
887
+ */
888
+ do_action( 'set_user_switching_cookie', $auth_cookie, $expiration, $old_user_id, $scheme, $token );
889
+
890
+ /**
891
+ * Fires immediately before the User Switching old user cookie is set.
892
+ *
893
+ * @since 1.4.0
894
+ *
895
+ * @param string $olduser_cookie The old user cookie value.
896
+ * @param int $expiration The time when the logged-in authentication cookie expires as a UNIX timestamp.
897
+ * Default is 48 hours from now.
898
+ * @param int $old_user_id User ID.
899
+ * @param string $scheme Authentication scheme. Default 'logged_in'.
900
+ * @param string $token User's session token to use for this cookie.
901
+ */
902
+ do_action( 'set_olduser_cookie', $olduser_cookie, $expiration, $old_user_id, 'logged_in', $token );
903
+
904
+ /** This filter is documented in wp-includes/pluggable.php */
905
+ if ( ! apply_filters( 'send_auth_cookies', true ) ) {
906
+ return;
907
+ }
908
+
909
+ setcookie( $auth_cookie_name, $auth_cookie, $expiration, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_auth_cookie, true );
910
+ setcookie( USER_SWITCHING_OLDUSER_COOKIE, $olduser_cookie, $expiration, COOKIEPATH, COOKIE_DOMAIN, $secure_olduser_cookie, true );
911
+ }
912
  }
913
 
914
  if ( ! function_exists( 'user_switching_clear_olduser_cookie' ) ) {
915
+ /**
916
+ * Clears the cookies containing the originating user, or pops the latest item off the end if there's more than one.
917
+ *
918
+ * @param bool $clear_all Optional. Whether to clear the cookies (as opposed to just popping the last user off the end). Default true.
919
+ */
920
+ function user_switching_clear_olduser_cookie( $clear_all = true ) {
921
+ $auth_cookie = user_switching_get_auth_cookie();
922
+ if ( ! empty( $auth_cookie ) ) {
923
+ array_pop( $auth_cookie );
924
+ }
925
+ if ( $clear_all || empty( $auth_cookie ) ) {
926
+ /**
927
+ * Fires just before the user switching cookies are cleared.
928
+ *
929
+ * @since 1.4.0
930
+ */
931
+ do_action( 'clear_olduser_cookie' );
932
 
933
+ /** This filter is documented in wp-includes/pluggable.php */
934
+ if ( ! apply_filters( 'send_auth_cookies', true ) ) {
935
+ return;
936
+ }
937
+
938
+ $expire = time() - 31536000;
939
+ setcookie( USER_SWITCHING_COOKIE, ' ', $expire, SITECOOKIEPATH, COOKIE_DOMAIN );
940
+ setcookie( USER_SWITCHING_SECURE_COOKIE, ' ', $expire, SITECOOKIEPATH, COOKIE_DOMAIN );
941
+ setcookie( USER_SWITCHING_OLDUSER_COOKIE, ' ', $expire, COOKIEPATH, COOKIE_DOMAIN );
942
  } else {
943
+ if ( user_switching::secure_auth_cookie() ) {
944
+ $scheme = 'secure_auth';
945
+ } else {
946
+ $scheme = 'auth';
947
+ }
948
 
949
+ $old_cookie = end( $auth_cookie );
950
+
951
+ $old_user_id = wp_validate_auth_cookie( $old_cookie, $scheme );
952
+ if ( $old_user_id ) {
953
+ $parts = wp_parse_auth_cookie( $old_cookie, $scheme );
954
+ user_switching_set_olduser_cookie( $old_user_id, true, $parts['token'] );
955
+ }
956
  }
957
  }
958
  }
 
959
 
960
  if ( ! function_exists( 'user_switching_get_olduser_cookie' ) ) {
961
+ /**
962
+ * Gets the value of the cookie containing the originating user.
963
+ *
964
+ * @return string|false The old user cookie, or boolean false if there isn't one.
965
+ */
966
+ function user_switching_get_olduser_cookie() {
967
+ if ( isset( $_COOKIE[ USER_SWITCHING_OLDUSER_COOKIE ] ) ) {
968
+ return wp_unslash( $_COOKIE[ USER_SWITCHING_OLDUSER_COOKIE ] ); // WPCS: sanitization ok
969
+ } else {
970
+ return false;
971
+ }
972
  }
973
  }
 
974
 
975
  if ( ! function_exists( 'user_switching_get_auth_cookie' ) ) {
976
+ /**
977
+ * Gets the value of the auth cookie containing the list of originating users.
978
+ *
979
+ * @return string[] Array of originating user authentication cookie values. Empty array if there are none.
980
+ */
981
+ function user_switching_get_auth_cookie() {
982
+ if ( user_switching::secure_auth_cookie() ) {
983
+ $auth_cookie_name = USER_SWITCHING_SECURE_COOKIE;
984
+ } else {
985
+ $auth_cookie_name = USER_SWITCHING_COOKIE;
986
+ }
987
 
988
+ if ( isset( $_COOKIE[ $auth_cookie_name ] ) && is_string( $_COOKIE[ $auth_cookie_name ] ) ) {
989
+ $cookie = json_decode( wp_unslash( $_COOKIE[ $auth_cookie_name ] ) ); // WPCS: sanitization ok
990
+ }
991
+ if ( ! isset( $cookie ) || ! is_array( $cookie ) ) {
992
+ $cookie = array();
993
+ }
994
+ return $cookie;
995
  }
 
 
996
  }
997
 
998
  if ( ! function_exists( 'switch_to_user' ) ) {
999
+ /**
1000
+ * Switches the current logged in user to the specified user.
1001
+ *
1002
+ * @param int $user_id The ID of the user to switch to.
1003
+ * @param bool $remember Optional. Whether to 'remember' the user in the form of a persistent browser cookie. Default false.
1004
+ * @param bool $set_old_user Optional. Whether to set the old user cookie. Default true.
1005
+ * @return false|WP_User WP_User object on success, false on failure.
1006
+ */
1007
+ function switch_to_user( $user_id, $remember = false, $set_old_user = true ) {
1008
+ $user = get_userdata( $user_id );
 
 
 
 
1009
 
1010
+ if ( ! $user ) {
1011
+ return false;
1012
+ }
1013
 
1014
+ $old_user_id = ( is_user_logged_in() ) ? get_current_user_id() : false;
1015
+ $old_token = function_exists( 'wp_get_session_token' ) ? wp_get_session_token() : '';
1016
+ $auth_cookie = user_switching_get_auth_cookie();
1017
+ $cookie_parts = wp_parse_auth_cookie( end( $auth_cookie ) );
 
1018
 
1019
+ if ( $set_old_user && $old_user_id ) {
1020
+ // Switching to another user
1021
+ $new_token = '';
1022
+ user_switching_set_olduser_cookie( $old_user_id, false, $old_token );
1023
+ } else {
1024
+ // Switching back, either after being switched off or after being switched to another user
1025
+ $new_token = isset( $cookie_parts['token'] ) ? $cookie_parts['token'] : '';
1026
+ user_switching_clear_olduser_cookie( false );
1027
+ }
1028
 
 
1029
  /**
1030
+ * Attaches the original user ID and session token to the new session when a user switches to another user.
 
 
 
 
 
 
 
 
 
 
 
 
1031
  *
1032
+ * @param array $session Array of extra data.
1033
+ * @param int $user_id User ID.
1034
+ * @return array Array of extra data.
1035
  */
1036
+ $session_filter = function( array $session, $user_id ) use ( $old_user_id, $old_token ) {
1037
+ $session['switched_from_id'] = $old_user_id;
1038
+ $session['switched_from_session'] = $old_token;
1039
+ return $session;
1040
+ };
1041
 
1042
+ add_filter( 'attach_session_information', $session_filter, 99, 2 );
 
 
1043
 
1044
+ wp_clear_auth_cookie();
1045
+ wp_set_auth_cookie( $user_id, $remember, '', $new_token );
1046
+ wp_set_current_user( $user_id );
 
 
 
 
 
 
1047
 
1048
+ remove_filter( 'attach_session_information', $session_filter, 99 );
1049
+
1050
+ if ( $set_old_user ) {
1051
+ /**
1052
+ * Fires when a user switches to another user account.
1053
+ *
1054
+ * @since 0.6.0
1055
+ * @since 1.4.0 The `$new_token` and `$old_token` parameters were added.
1056
+ *
1057
+ * @param int $user_id The ID of the user being switched to.
1058
+ * @param int $old_user_id The ID of the user being switched from.
1059
+ * @param string $new_token The token of the session of the user being switched to. Can be an empty string
1060
+ * or a token for a session that may or may not still be valid.
1061
+ * @param string $old_token The token of the session of the user being switched from.
1062
+ */
1063
+ do_action( 'switch_to_user', $user_id, $old_user_id, $new_token, $old_token );
1064
+ } else {
1065
+ /**
1066
+ * Fires when a user switches back to their originating account.
1067
+ *
1068
+ * @since 0.6.0
1069
+ * @since 1.4.0 The `$new_token` and `$old_token` parameters were added.
1070
+ *
1071
+ * @param int $user_id The ID of the user being switched back to.
1072
+ * @param int|false $old_user_id The ID of the user being switched from, or false if the user is switching back
1073
+ * after having been switched off.
1074
+ * @param string $new_token The token of the session of the user being switched to. Can be an empty string
1075
+ * or a token for a session that may or may not still be valid.
1076
+ * @param string $old_token The token of the session of the user being switched from.
1077
+ */
1078
+ do_action( 'switch_back_user', $user_id, $old_user_id, $new_token, $old_token );
1079
+ }
1080
+
1081
+ if ( $old_token && $old_user_id && ! $set_old_user ) {
1082
+ // When switching back, destroy the session for the old user
1083
+ $manager = WP_Session_Tokens::get_instance( $old_user_id );
1084
+ $manager->destroy( $old_token );
1085
+ }
1086
 
1087
+ return $user;
1088
+ }
1089
+ }
1090
 
1091
+ if ( ! function_exists( 'switch_off_user' ) ) {
1092
  /**
1093
+ * Switches off the current logged in user. This logs the current user out while retaining a cookie allowing them to log
1094
+ * straight back in using the 'Switch back to {user}' system.
1095
  *
1096
+ * @return bool True on success, false on failure.
 
 
1097
  */
1098
+ function switch_off_user() {
1099
+ $old_user_id = get_current_user_id();
1100
 
1101
+ if ( ! $old_user_id ) {
1102
+ return false;
1103
+ }
1104
+
1105
+ $old_token = function_exists( 'wp_get_session_token' ) ? wp_get_session_token() : '';
1106
+
1107
+ user_switching_set_olduser_cookie( $old_user_id, false, $old_token );
1108
+ wp_clear_auth_cookie();
1109
+ wp_set_current_user( 0 );
1110
+
1111
+ /**
1112
+ * Fires when a user switches off.
1113
+ *
1114
+ * @since 0.6.0
1115
+ * @since 1.4.0 The `$old_token` parameter was added.
1116
+ *
1117
+ * @param int $old_user_id The ID of the user switching off.
1118
+ * @param string $old_token The token of the session of the user switching off.
1119
+ */
1120
+ do_action( 'switch_off_user', $old_user_id, $old_token );
1121
+
1122
+ return true;
1123
+ }
1124
  }
1125
 
1126
  if ( ! function_exists( 'current_user_switched' ) ) {
1127
+ /**
1128
+ * Returns whether or not the current user switched into their account.
1129
+ *
1130
+ * @return false|WP_User False if the user isn't logged in or they didn't switch in; old user object (which evaluates to
1131
+ * true) if the user switched into the current user account.
1132
+ */
1133
+ function current_user_switched() {
1134
+ if ( ! is_user_logged_in() ) {
1135
+ return false;
1136
+ }
1137
 
1138
+ return user_switching::get_old_user();
1139
+ }
1140
  }
1141
 
1142
  $GLOBALS['user_switching'] = user_switching::get_instance();
1143
+ $GLOBALS['user_switching']->init_hooks();