Completed
Push — update/performence-remove-secu... ( 79e64b...0dfbce )
by
unknown
155:58 queued 147:23
created

Jetpack_SSO::module_configuration_screen()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 0
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
require_once( JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' );
3
4
/**
5
 * Module Name: Single Sign On
6
 * Module Description: Secure user authentication.
7
 * Jumpstart Description: Lets you log in to all your Jetpack-enabled sites with one click using your WordPress.com account.
8
 * Sort Order: 30
9
 * Recommendation Order: 5
10
 * First Introduced: 2.6
11
 * Requires Connection: Yes
12
 * Auto Activate: No
13
 * Module Tags: Developers
14
 * Feature: Jumpstart, Performance-Security
15
 * Additional Search Queries: sso, single sign on, login, log in
16
 */
17
18
class Jetpack_SSO {
19
	static $instance = null;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $instance.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
20
21
	private function __construct() {
22
23
		self::$instance = $this;
24
25
		add_action( 'admin_init',             array( $this, 'maybe_authorize_user_after_sso' ), 1 );
26
		add_action( 'admin_init',             array( $this, 'register_settings' ) );
27
		add_action( 'login_init',             array( $this, 'login_init' ) );
28
		add_action( 'delete_user',            array( $this, 'delete_connection_for_user' ) );
29
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
30
		add_action( 'init',                   array( $this, 'maybe_logout_user' ), 5 );
31
		add_action( 'jetpack_modules_loaded', array( $this, 'module_configure_button' ) );
32
		add_action( 'admin_enqueue_scripts',  array( $this, 'admin_enqueue_scripts' ) );
33
		add_action( 'login_form_logout',      array( $this, 'store_wpcom_profile_cookies_on_logout' ) );
34
		add_action( 'wp_login',               array( 'Jetpack_SSO', 'clear_wpcom_profile_cookies' ) );
35
		add_action( 'jetpack_unlinked_user',  array( $this, 'delete_connection_for_user') );
36
37
		// Adding this action so that on login_init, the action won't be sanitized out of the $action global.
38
		add_action( 'login_form_jetpack-sso', '__return_true' );
39
	}
40
41
	/**
42
	 * Returns the single instance of the Jetpack_SSO object
43
	 *
44
	 * @since 2.8
45
	 * @return Jetpack_SSO
46
	 **/
47
	public static function get_instance() {
48
		if ( ! is_null( self::$instance ) ) {
49
			return self::$instance;
50
		}
51
52
		return self::$instance = new Jetpack_SSO;
53
	}
54
55
	/**
56
	 * Add configure button and functionality to the module card on the Jetpack screen
57
	 **/
58
	public static function module_configure_button() {
59
		Jetpack::enable_module_configurable( __FILE__ );
60
		Jetpack::module_configuration_load( __FILE__, array( __CLASS__, 'module_configuration_load' ) );
61
		Jetpack::module_configuration_head( __FILE__, array( __CLASS__, 'module_configuration_head' ) );
62
		Jetpack::module_configuration_screen( __FILE__, array( __CLASS__, 'module_configuration_screen' ) );
63
	}
64
65
	public static function module_configuration_load() {}
66
67
	public static function module_configuration_head() {}
68
69
	public static function module_configuration_screen() {
70
		?>
71
		<form method="post" action="options.php">
72
			<?php settings_fields( 'jetpack-sso' ); ?>
73
			<?php do_settings_sections( 'jetpack-sso' ); ?>
74
			<?php submit_button(); ?>
75
		</form>
76
		<?php
77
	}
78
79
80
	/**
81
	 * When the default login form is hidden, this method is called on the 'authenticate' filter with a priority of 30.
82
	 * This method disables the ability to submit the default login form.
83
	 *
84
	 * @param $user
85
	 *
86
	 * @return WP_Error
87
	 */
88
	public function disable_default_login_form( $user ) {
89
		if ( is_wp_error( $user ) ) {
90
			return $user;
91
		}
92
93
		/**
94
		 * Since we're returning an error that will be shown as a red notice, let's remove the
95
		 * informational "blue" notice.
96
		 */
97
		remove_filter( 'login_message', array( $this, 'msg_login_by_jetpack' ) );
98
		return new WP_Error( 'jetpack_sso_required', $this->get_sso_required_message() );
99
	}
100
101
	/**
102
	 * If jetpack_force_logout == 1 in current user meta the user will be forced
103
	 * to logout and reauthenticate with the site.
104
	 **/
105
	public function maybe_logout_user() {
106
		global $current_user;
107
108
		if ( 1 == $current_user->jetpack_force_logout ) {
109
			delete_user_meta( $current_user->ID, 'jetpack_force_logout' );
110
			self::delete_connection_for_user( $current_user->ID );
111
			wp_logout();
112
			wp_safe_redirect( wp_login_url() );
113
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method maybe_logout_user() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
114
		}
115
	}
116
117
118
	/**
119
	 * Adds additional methods the WordPress xmlrpc API for handling SSO specific features
120
	 *
121
	 * @param array $methods
122
	 * @return array
123
	 **/
124
	public function xmlrpc_methods( $methods ) {
125
		$methods['jetpack.userDisconnect'] = array( $this, 'xmlrpc_user_disconnect' );
126
		return $methods;
127
	}
128
129
	/**
130
	 * Marks a user's profile for disconnect from WordPress.com and forces a logout
131
	 * the next time the user visits the site.
132
	 **/
133
	public function xmlrpc_user_disconnect( $user_id ) {
134
		$user_query = new WP_User_Query(
135
			array(
136
				'meta_key' => 'wpcom_user_id',
137
				'meta_value' => $user_id,
138
			)
139
		);
140
		$user = $user_query->get_results();
141
		$user = $user[0];
142
143
		if ( $user instanceof WP_User ) {
0 ignored issues
show
Bug introduced by
The class WP_User does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
144
			$user = wp_set_current_user( $user->ID );
145
			update_user_meta( $user->ID, 'jetpack_force_logout', '1' );
146
			self::delete_connection_for_user( $user->ID );
147
			return true;
148
		}
149
		return false;
150
	}
151
152
	/**
153
	 * Enqueues scripts and styles necessary for SSO login.
154
	 */
155
	public function login_enqueue_scripts() {
156
		global $action;
157
158
		if ( ! in_array( $action, array( 'jetpack-sso', 'login' ) ) ) {
159
			return;
160
		}
161
162
		if ( is_rtl() ) {
163
			wp_enqueue_style( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login-rtl.css', JETPACK__PLUGIN_FILE ), array( 'login', 'genericons' ), JETPACK__VERSION );
164
		} else {
165
			wp_enqueue_style( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.css', JETPACK__PLUGIN_FILE ), array( 'login', 'genericons' ), JETPACK__VERSION );
166
		}
167
168
		wp_enqueue_script( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.js', JETPACK__PLUGIN_FILE ), array( 'jquery' ), JETPACK__VERSION );
169
	}
170
171
	/**
172
	 * Enqueue styles neceessary for Jetpack SSO on users' profiles
173
	 */
174
	public function admin_enqueue_scripts() {
175
		$screen = get_current_screen();
176
177
		if ( empty( $screen ) || ! in_array( $screen->base, array( 'edit-user', 'profile' ) ) ) {
178
			return;
179
		}
180
181
		wp_enqueue_style( 'jetpack-sso-profile', plugins_url( 'modules/sso/jetpack-sso-profile.css', JETPACK__PLUGIN_FILE ), array( 'genericons' ), JETPACK__VERSION );
182
	}
183
184
	/**
185
	 * Adds Jetpack SSO classes to login body
186
	 *
187
	 * @param  array $classes Array of classes to add to body tag
188
	 * @return array          Array of classes to add to body tag
189
	 */
190
	public function login_body_class( $classes ) {
191
		global $action;
192
193
		if ( ! in_array( $action, array( 'jetpack-sso', 'login' ) ) ) {
194
			return $classes;
195
		}
196
197
		// Always add the jetpack-sso class so that we can add SSO specific styling even when the SSO form isn't being displayed.
198
		$classes[] = 'jetpack-sso';
199
200
		/**
201
		 * Should we show the SSO login form?
202
		 *
203
		 * $_GET['jetpack-sso-default-form'] is used to provide a fallback in case JavaScript is not enabled.
204
		 *
205
		 * The default_to_sso_login() method allows us to dynamically decide whether we show the SSO login form or not.
206
		 * The SSO module uses the method to display the default login form if we can not find a user to log in via SSO.
207
		 * But, the method could be filtered by a site admin to always show the default login form if that is preferred.
208
		 */
209
		if ( empty( $_GET['jetpack-sso-show-default-form'] ) && Jetpack_SSO_Helpers::show_sso_login() ) {
210
			$classes[] = 'jetpack-sso-form-display';
211
		}
212
213
		return $classes;
214
	}
215
216
	/**
217
	 * Adds settings fields to Settings > General > Single Sign On that allows users to
218
	 * turn off the login form on wp-login.php
219
	 *
220
	 * @since 2.7
221
	 **/
222
	public function register_settings() {
223
224
		add_settings_section(
225
			'jetpack_sso_settings',
226
			__( 'Single Sign On' , 'jetpack' ),
227
			'__return_false',
228
			'jetpack-sso'
229
		);
230
231
		/*
232
		 * Settings > General > Single Sign On
233
		 * Require two step authentication
234
		 */
235
		register_setting(
236
			'jetpack-sso',
237
			'jetpack_sso_require_two_step',
238
			array( $this, 'validate_jetpack_sso_require_two_step' )
239
		);
240
241
		add_settings_field(
242
			'jetpack_sso_require_two_step',
243
			'', // __( 'Require Two-Step Authentication' , 'jetpack' ),
244
			array( $this, 'render_require_two_step' ),
245
			'jetpack-sso',
246
			'jetpack_sso_settings'
247
		);
248
249
		/*
250
		 * Settings > General > Single Sign On
251
		 */
252
		register_setting(
253
			'jetpack-sso',
254
			'jetpack_sso_match_by_email',
255
			array( $this, 'validate_jetpack_sso_match_by_email' )
256
		);
257
258
		add_settings_field(
259
			'jetpack_sso_match_by_email',
260
			'', // __( 'Match by Email' , 'jetpack' ),
261
			array( $this, 'render_match_by_email' ),
262
			'jetpack-sso',
263
			'jetpack_sso_settings'
264
		);
265
	}
266
267
	/**
268
	 * Builds the display for the checkbox allowing user to require two step
269
	 * auth be enabled on WordPress.com accounts before login. Displays in Settings > General
270
	 *
271
	 * @since 2.7
272
	 **/
273 View Code Duplication
	public function render_require_two_step() {
274
		/** This filter is documented in modules/sso.php */
275
		$require_two_step = Jetpack_SSO_Helpers::is_two_step_required();
276
		$disabled = $require_two_step ? ' disabled="disabled"' : '';
277
		echo '<label>';
278
		echo '<input type="checkbox" name="jetpack_sso_require_two_step" ' . checked( $require_two_step, true, false ) . "$disabled>";
279
		esc_html_e( 'Require Two-Step Authentication' , 'jetpack' );
280
		echo '</label>';
281
	}
282
283
	/**
284
	 * Validate the require  two step checkbox in Settings > General
285
	 *
286
	 * @since 2.7
287
	 * @return boolean
288
	 **/
289
	public function validate_jetpack_sso_require_two_step( $input ) {
290
		return ( ! empty( $input ) ) ? 1 : 0;
291
	}
292
293
	/**
294
	 * Builds the display for the checkbox allowing the user to allow matching logins by email
295
	 * Displays in Settings > General
296
	 *
297
	 * @since 2.9
298
	 **/
299 View Code Duplication
	public function render_match_by_email() {
300
		$match_by_email = 1 == Jetpack_SSO_Helpers::match_by_email();
301
		$disabled = $match_by_email ? ' disabled="disabled"' : '';
302
		echo '<label>';
303
		echo '<input type="checkbox" name="jetpack_sso_match_by_email"' . checked( $match_by_email, true, false ) . "$disabled>";
304
		esc_html_e( 'Match by Email', 'jetpack' );
305
		echo '</label>';
306
	}
307
308
	/**
309
	 * Validate the match by email check in Settings > General
310
	 *
311
	 * @since 2.9
312
	 * @return boolean
313
	 **/
314
	public function validate_jetpack_sso_match_by_email( $input ) {
315
		return ( ! empty( $input ) ) ? 1 : 0;
316
	}
317
318
	/**
319
	 * Checks to determine if the user wants to login on wp-login
320
	 *
321
	 * This function mostly exists to cover the exceptions to login
322
	 * that may exist as other parameters to $_GET[action] as $_GET[action]
323
	 * does not have to exist. By default WordPress assumes login if an action
324
	 * is not set, however this may not be true, as in the case of logout
325
	 * where $_GET[loggedout] is instead set
326
	 *
327
	 * @return boolean
328
	 **/
329
	private function wants_to_login() {
330
		$wants_to_login = false;
331
332
		// Cover default WordPress behavior
333
		$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'login';
334
335
		// And now the exceptions
336
		$action = isset( $_GET['loggedout'] ) ? 'loggedout' : $action;
337
338
		if ( 'login' == $action ) {
339
			$wants_to_login = true;
340
		}
341
342
		return $wants_to_login;
343
	}
344
345
	function login_init() {
346
		global $action;
347
348
		if ( Jetpack_SSO_Helpers::should_hide_login_form() ) {
349
			/**
350
			 * Since the default authenticate filters fire at priority 20 for checking username and password,
351
			 * let's fire at priority 30. wp_authenticate_spam_check is fired at priority 99, but since we return a
352
			 * WP_Error in disable_default_login_form, then we won't trigger spam processing logic.
353
			 */
354
			add_filter( 'authenticate', array( $this, 'disable_default_login_form' ), 30 );
355
356
			/**
357
			 * Filter the display of the disclaimer message appearing when default WordPress login form is disabled.
358
			 *
359
			 * @module sso
360
			 *
361
			 * @since 2.8.0
362
			 *
363
			 * @param bool true Should the disclaimer be displayed. Default to true.
364
			 */
365
			$display_sso_disclaimer = apply_filters( 'jetpack_sso_display_disclaimer', true );
366
			if ( $display_sso_disclaimer ) {
367
				add_filter( 'login_message', array( $this, 'msg_login_by_jetpack' ) );
368
			}
369
		}
370
371
		/**
372
		 * If the user is attempting to logout AND the auto-forward to WordPress.com
373
		 * login is set then we need to ensure we do not auto-forward the user and get
374
		 * them stuck in an infinite logout loop.
375
		 */
376
		if ( isset( $_GET['loggedout'] ) && Jetpack_SSO_Helpers::bypass_login_forward_wpcom() ) {
377
			add_filter( 'jetpack_remove_login_form', '__return_true' );
378
		}
379
380
		/**
381
		 * Check to see if the site admin wants to automagically forward the user
382
		 * to the WordPress.com login page AND  that the request to wp-login.php
383
		 * is not something other than login (Like logout!)
384
		 */
385 View Code Duplication
		if (
386
			$this->wants_to_login()
387
			&& Jetpack_SSO_Helpers::bypass_login_forward_wpcom()
388
		) {
389
			add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
390
			$this->maybe_save_cookie_redirect();
391
			$reauth = ! empty( $_GET['force_reauth'] );
392
			$sso_url = $this->get_sso_url_or_die( $reauth );
393
			JetpackTracking::record_user_event( 'sso_login_redirect_bypass_success' );
394
			wp_safe_redirect( $sso_url );
395
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method login_init() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
396
		}
397
398
		if ( 'login' === $action ) {
399
			$this->display_sso_login_form();
400
		} elseif ( 'jetpack-sso' === $action ) {
401
			if ( isset( $_GET['result'], $_GET['user_id'], $_GET['sso_nonce'] ) && 'success' == $_GET['result'] ) {
402
				$this->handle_login();
403
				$this->display_sso_login_form();
404
			} else {
405
				if ( Jetpack::check_identity_crisis() ) {
406
					JetpackTracking::record_user_event( 'sso_login_redirect_failed', array(
407
						'error_message' => 'identity_crisis'
408
					) );
409
					wp_die( __( "Error: This site's Jetpack connection is currently experiencing problems.", 'jetpack' ) );
410 View Code Duplication
				} else {
411
					$this->maybe_save_cookie_redirect();
412
					// Is it wiser to just use wp_redirect than do this runaround to wp_safe_redirect?
413
					add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
414
					$reauth = ! empty( $_GET['force_reauth'] );
415
					$sso_url = $this->get_sso_url_or_die( $reauth );
416
					JetpackTracking::record_user_event( 'sso_login_redirect_success' );
417
					wp_safe_redirect( $sso_url );
418
					exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method login_init() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
419
				}
420
			}
421
		}
422
	}
423
424
	/**
425
	 * Ensures that we can get a nonce from WordPress.com via XML-RPC before setting
426
	 * up the hooks required to display the SSO form.
427
	 */
428
	public function display_sso_login_form() {
429
		$sso_nonce = self::request_initial_nonce();
430
		if ( is_wp_error( $sso_nonce ) ) {
431
			return;
432
		}
433
434
		add_action( 'login_form',            array( $this, 'login_form' ) );
435
		add_filter( 'login_body_class',      array( $this, 'login_body_class' ) );
436
		add_action( 'login_enqueue_scripts', array( $this, 'login_enqueue_scripts' ) );
437
	}
438
439
	/**
440
	 * Conditionally save the redirect_to url as a cookie.
441
	 */
442
	public static function maybe_save_cookie_redirect() {
443
		if ( headers_sent() ) {
444
			return new WP_Error( 'headers_sent', __( 'Cannot deal with cookie redirects, as headers are already sent.', 'jetpack' ) );
445
		}
446
447
		if ( ! empty( $_GET['redirect_to'] ) ) {
448
			// If we have something to redirect to
449
			$url = esc_url_raw( $_GET['redirect_to'] );
450
			setcookie( 'jetpack_sso_redirect_to', $url, time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
451
452
		} elseif ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
453
			// Otherwise, if it's already set, purge it.
454
			setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
455
		}
456
457
		if ( ! empty( $_GET['rememberme'] ) ) {
458
			setcookie( 'jetpack_sso_remember_me', '1', time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
459
		} elseif ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
460
			setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
461
		}
462
	}
463
464
	/**
465
	 * Outputs the Jetpack SSO button and description as well as the toggle link
466
	 * for switching between Jetpack SSO and default login.
467
	 */
468
	function login_form() {
469
		$site_name = get_bloginfo( 'name' );
470
		if ( ! $site_name ) {
471
			$site_name = get_bloginfo( 'url' );
472
		}
473
474
		$display_name = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] )
475
			? $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ]
476
			: false;
477
		$gravatar = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] )
478
			? $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ]
479
			: false;
480
481
		?>
482
		<div id="jetpack-sso-wrap">
483
			<?php if ( $display_name && $gravatar ) : ?>
484
				<div id="jetpack-sso-wrap__user">
485
					<img width="72" height="72" src="<?php echo esc_html( $gravatar ); ?>" />
486
487
					<h2>
488
						<?php
489
							echo wp_kses(
490
								sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
491
								array( 'span' => true )
492
							);
493
						?>
494
					</h2>
495
				</div>
496
497
			<?php endif; ?>
498
499
500
			<div id="jetpack-sso-wrap__action">
501
				<?php echo $this->build_sso_button( array(), 'is_primary' ); ?>
0 ignored issues
show
Documentation introduced by
'is_primary' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
502
503
				<?php if ( $display_name && $gravatar ) : ?>
504
					<a rel="nofollow" class="jetpack-sso-wrap__reauth" href="<?php echo esc_url( $this->build_sso_button_url( array( 'force_reauth' => '1' ) ) ); ?>">
505
						<?php esc_html_e( 'Log in as a different WordPress.com user', 'jetpack' ); ?>
506
					</a>
507
				<?php else : ?>
508
					<p>
509
						<?php
510
							echo esc_html(
511
								sprintf(
512
									__( 'You can now save time spent logging in by connecting your WordPress.com account to %s.', 'jetpack' ),
513
									esc_html( $site_name )
514
								)
515
							);
516
						?>
517
					</p>
518
				<?php endif; ?>
519
			</div>
520
521
			<?php if ( ! Jetpack_SSO_Helpers::should_hide_login_form() ) : ?>
522
				<div class="jetpack-sso-or">
523
					<span><?php esc_html_e( 'Or', 'jetpack' ); ?></span>
524
				</div>
525
526
				<a href="<?php echo add_query_arg( 'jetpack-sso-show-default-form', '1' ); ?>" class="jetpack-sso-toggle wpcom">
527
					<?php
528
						esc_html_e( 'Log in with username and password', 'jetpack' )
529
					?>
530
				</a>
531
532
				<a href="<?php echo add_query_arg( 'jetpack-sso-show-default-form', '0' ); ?>" class="jetpack-sso-toggle default">
533
					<?php
534
						esc_html_e( 'Log in with WordPress.com', 'jetpack' )
535
					?>
536
				</a>
537
			<?php endif; ?>
538
		</div>
539
		<?php
540
	}
541
542
	/**
543
	 * Clear the cookies that store the profile information for the last
544
	 * WPCOM user to connect.
545
	 */
546
	static function clear_wpcom_profile_cookies() {
547 View Code Duplication
		if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) ) {
548
			setcookie(
549
				'jetpack_sso_wpcom_name_' . COOKIEHASH,
550
				' ',
551
				time() - YEAR_IN_SECONDS,
552
				COOKIEPATH,
553
				COOKIE_DOMAIN
554
			);
555
		}
556
557 View Code Duplication
		if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) ) {
558
			setcookie(
559
				'jetpack_sso_wpcom_gravatar_' . COOKIEHASH,
560
				' ',
561
				time() - YEAR_IN_SECONDS,
562
				COOKIEPATH,
563
				COOKIE_DOMAIN
564
			);
565
		}
566
	}
567
568
	static function delete_connection_for_user( $user_id ) {
569
		if ( ! $wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true ) ) {
570
			return;
571
		}
572
		Jetpack::load_xml_rpc_client();
573
		$xml = new Jetpack_IXR_Client( array(
574
			'wpcom_user_id' => $user_id,
575
		) );
576
		$xml->query( 'jetpack.sso.removeUser', $wpcom_user_id );
577
578
		if ( $xml->isError() ) {
579
			return false;
580
		}
581
582
		// Clean up local data stored for SSO
583
		delete_user_meta( $user_id, 'wpcom_user_id' );
584
		delete_user_meta( $user_id, 'wpcom_user_data'  );
585
		self::clear_wpcom_profile_cookies();
586
587
		return $xml->getResponse();
588
	}
589
590 View Code Duplication
	static function request_initial_nonce() {
591
		Jetpack::load_xml_rpc_client();
592
		$xml = new Jetpack_IXR_Client( array(
593
			'user_id' => get_current_user_id(),
594
		) );
595
		$xml->query( 'jetpack.sso.requestNonce' );
596
597
		if ( $xml->isError() ) {
598
			return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() );
599
		}
600
601
		return $xml->getResponse();
602
	}
603
604
	/**
605
	 * The function that actually handles the login!
606
	 */
607
	function handle_login() {
608
		$wpcom_nonce   = sanitize_key( $_GET['sso_nonce'] );
609
		$wpcom_user_id = (int) $_GET['user_id'];
610
611
		Jetpack::load_xml_rpc_client();
612
		$xml = new Jetpack_IXR_Client( array(
613
			'user_id' => get_current_user_id(),
614
		) );
615
		$xml->query( 'jetpack.sso.validateResult', $wpcom_nonce, $wpcom_user_id );
616
617
		if ( $xml->isError() ) {
618
			$error_message = sanitize_text_field(
619
				sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() )
620
			);
621
			JetpackTracking::record_user_event( 'sso_login_failed', array(
622
				'error_message' => $error_message
623
			) );
624
			wp_die( $error_message );
625
		}
626
627
		$user_data = $xml->getResponse();
628
629
		if ( empty( $user_data ) ) {
630
			JetpackTracking::record_user_event( 'sso_login_failed', array(
631
				'error_message' => 'invalid_response_data'
632
			) );
633
			wp_die( __( 'Error, invalid response data.', 'jetpack' ) );
634
		}
635
636
		$user_data = (object) $user_data;
637
		$user = null;
638
639
		/**
640
		 * Fires before Jetpack's SSO modifies the log in form.
641
		 *
642
		 * @module sso
643
		 *
644
		 * @since 2.6.0
645
		 *
646
		 * @param object $user_data User login information.
647
		 */
648
		do_action( 'jetpack_sso_pre_handle_login', $user_data );
649
650
		if ( Jetpack_SSO_Helpers::is_two_step_required() && 0 === (int) $user_data->two_step_enabled ) {
651
			$this->user_data = $user_data;
0 ignored issues
show
Bug introduced by
The property user_data does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
652
653
			JetpackTracking::record_user_event( 'sso_login_failed', array(
654
				'error_message' => 'error_msg_enable_two_step'
655
			) );
656
657
			/** This filter is documented in core/src/wp-includes/pluggable.php */
658
			do_action( 'wp_login_failed', $user_data->login );
659
			add_filter( 'login_message', array( $this, 'error_msg_enable_two_step' ) );
660
			return;
661
		}
662
663
		$user_found_with = '';
664
		if ( empty( $user ) && isset( $user_data->external_user_id ) ) {
665
			$user_found_with = 'external_user_id';
666
			$user = get_user_by( 'id', intval( $user_data->external_user_id ) );
667
			if ( $user ) {
668
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
669
			}
670
		}
671
672
		// If we don't have one by wpcom_user_id, try by the email?
673
		if ( empty( $user ) && Jetpack_SSO_Helpers::match_by_email() ) {
674
			$user_found_with = 'match_by_email';
675
			$user = get_user_by( 'email', $user_data->email );
676
			if ( $user ) {
677
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
678
			}
679
		}
680
681
		// If we've still got nothing, create the user.
682
		if ( empty( $user ) && ( get_option( 'users_can_register' ) || Jetpack_SSO_Helpers::new_user_override() ) ) {
683
			// If not matching by email we still need to verify the email does not exist
684
			// or this blows up
685
			/**
686
			 * If match_by_email is true, we know the email doesn't exist, as it would have
687
			 * been found in the first pass.  If get_user_by( 'email' ) doesn't find the
688
			 * user, then we know that email is unused, so it's safe to add.
689
			 */
690
			if ( Jetpack_SSO_Helpers::match_by_email() || ! get_user_by( 'email', $user_data->email ) ) {
691
				$username = $user_data->login;
692
693
				if ( username_exists( $username ) ) {
694
					$username = $user_data->login . '_' . $user_data->ID;
695
				}
696
697
				$tries = 0;
698
				while ( username_exists( $username ) ) {
699
					$username = $user_data->login . '_' . $user_data->ID . '_' . mt_rand();
700
					if ( $tries++ >= 5 ) {
701
						JetpackTracking::record_user_event( 'sso_login_failed', array(
702
							'error_message' => 'could_not_create_username'
703
						) );
704
						wp_die( __( "Error: Couldn't create suitable username.", 'jetpack' ) );
705
					}
706
				}
707
708
				$user_found_with = Jetpack_SSO_Helpers::new_user_override()
709
					? 'user_created_new_user_override'
710
					: 'user_created_users_can_register';
711
712
				$password = wp_generate_password( 20 );
713
				$user_id  = wp_create_user( $username, $password, $user_data->email );
714
				$user     = get_userdata( $user_id );
715
716
				$user->display_name = $user_data->display_name;
717
				$user->first_name   = $user_data->first_name;
718
				$user->last_name    = $user_data->last_name;
719
				$user->url          = $user_data->url;
720
				$user->description  = $user_data->description;
721
				wp_update_user( $user );
722
723
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
724
			} else {
725
				JetpackTracking::record_user_event( 'sso_login_failed', array(
726
					'error_message' => 'error_msg_email_already_exists'
727
				) );
728
729
				$this->user_data = $user_data;
730
				add_action( 'login_message', array( $this, 'error_msg_email_already_exists' ) );
731
				return;
732
			}
733
		}
734
735
		/**
736
		 * Fires after we got login information from WordPress.com.
737
		 *
738
		 * @module sso
739
		 *
740
		 * @since 2.6.0
741
		 *
742
		 * @param array $user WordPress.com User information.
743
		 * @param object $user_data User Login information.
744
		 */
745
		do_action( 'jetpack_sso_handle_login', $user, $user_data );
746
747
		if ( $user ) {
748
			// Cache the user's details, so we can present it back to them on their user screen
749
			update_user_meta( $user->ID, 'wpcom_user_data', $user_data );
750
751
			$remember = false;
752 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
753
				$remember = true;
754
				// And then purge it
755
				setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
756
			}
757
			/**
758
			 * Filter the remember me value.
759
			 *
760
			 * @module sso
761
			 *
762
			 * @since 2.8.0
763
			 *
764
			 * @param bool $remember Is the remember me option checked?
765
			 */
766
			$remember = apply_filters( 'jetpack_remember_login', $remember );
767
			wp_set_auth_cookie( $user->ID, $remember );
768
769
			/** This filter is documented in core/src/wp-includes/user.php */
770
			do_action( 'wp_login', $user->user_login, $user );
771
772
			wp_set_current_user( $user->ID );
773
774
			$_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( $_REQUEST['redirect_to'] ) : '';
775
			$redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : self::profile_page_url();
776
777
			// If we have a saved redirect to request in a cookie
778 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
779
				// Set that as the requested redirect to
780
				$redirect_to = $_request_redirect_to = esc_url_raw( $_COOKIE['jetpack_sso_redirect_to'] );
781
				// And then purge it
782
				setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
783
			}
784
785
			$is_user_connected = Jetpack::is_user_connected( $user->ID );
786
			JetpackTracking::record_user_event( 'sso_user_logged_in', array(
787
				'user_found_with' => $user_found_with,
788
				'user_connected'  => (bool) $is_user_connected,
789
				'user_role'       => Jetpack::init()->translate_current_user_to_role()
790
			) );
791
792
			if ( ! $is_user_connected ) {
793
				$calypso_env = ! empty( $_GET['calypso_env'] )
794
					? sanitize_key( $_GET['calypso_env'] )
795
					: '';
796
797
				wp_safe_redirect(
798
					add_query_arg(
799
						array(
800
							'redirect_to'               => $redirect_to,
801
							'request_redirect_to'       => $_request_redirect_to,
802
							'calypso_env'               => $calypso_env,
803
							'jetpack-sso-auth-redirect' => '1',
804
						),
805
						admin_url()
806
					)
807
				);
808
				exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method handle_login() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
809
			}
810
811
			wp_safe_redirect(
812
				/** This filter is documented in core/src/wp-login.php */
813
				apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user )
814
			);
815
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method handle_login() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
816
		}
817
818
		add_filter( 'jetpack_sso_default_to_sso_login', '__return_false' );
819
820
		JetpackTracking::record_user_event( 'sso_login_failed', array(
821
			'error_message' => 'cant_find_user'
822
		) );
823
824
		$this->user_data = $user_data;
825
		/** This filter is documented in core/src/wp-includes/pluggable.php */
826
		do_action( 'wp_login_failed', $user_data->login );
827
		add_filter( 'login_message', array( $this, 'cant_find_user' ) );
828
	}
829
830
	static function profile_page_url() {
831
		return admin_url( 'profile.php' );
832
	}
833
834
	function allowed_redirect_hosts( $hosts ) {
835
		if ( empty( $hosts ) ) {
836
			$hosts = array();
837
		}
838
		$hosts[] = 'wordpress.com';
839
		$hosts[] = 'jetpack.wordpress.com';
840
841
		return array_unique( $hosts );
842
	}
843
844
	/**
845
	 * Builds the "Login to WordPress.com" button that is displayed on the login page as well as user profile page.
846
	 *
847
	 * @param  array   $args       An array of arguments to add to the SSO URL.
848
	 * @param  boolean $is_primary Should the button have the `button-primary` class?
849
	 * @return string              Returns the HTML markup for the button.
850
	 */
851
	function build_sso_button( $args = array(), $is_primary = false ) {
852
		$url = $this->build_sso_button_url( $args );
853
		$classes = $is_primary
854
			? 'jetpack-sso button button-primary'
855
			: 'jetpack-sso button';
856
857
		return sprintf(
858
			'<a rel="nofollow" href="%1$s" class="%2$s"><span>%3$s %4$s</span></a>',
859
			esc_url( $url ),
860
			$classes,
861
			'<span class="genericon genericon-wordpress"></span>',
862
			esc_html__( 'Log in with WordPress.com', 'jetpack' )
863
		);
864
	}
865
866
	/**
867
	 * Builds a URL with `jetpack-sso` action and option args which is used to setup SSO.
868
	 *
869
	 * @param  array  $args An array of arguments to add to the SSO URL.
870
	 * @return string       The URL used for SSO.
871
	 */
872
	function build_sso_button_url( $args = array() ) {
873
		$defaults = array(
874
			'action'  => 'jetpack-sso',
875
		);
876
877
		$args = wp_parse_args( $args, $defaults );
878
879
		if ( ! empty( $_GET['redirect_to'] ) ) {
880
			$args['redirect_to'] = urlencode( esc_url_raw( $_GET['redirect_to'] ) );
881
		}
882
883
		return add_query_arg( $args, wp_login_url() );
884
	}
885
886
	/**
887
	 * Retrieves a WordPress.com SSO URL with appropriate query parameters or dies.
888
	 *
889
	 * @param  boolean  $reauth  Should the user be forced to reauthenticate on WordPress.com?
890
	 * @param  array    $args    Optional query parameters.
891
	 * @return string            The WordPress.com SSO URL.
892
	 */
893
	function get_sso_url_or_die( $reauth = false, $args = array() ) {
894
		if ( empty( $reauth ) ) {
895
			$sso_redirect = $this->build_sso_url( $args );
896
		} else {
897
			self::clear_wpcom_profile_cookies();
898
			$sso_redirect = $this->build_reauth_and_sso_url( $args );
899
		}
900
901
		// If there was an error retrieving the SSO URL, then error.
902
		if ( is_wp_error( $sso_redirect ) ) {
903
			$error_message = sanitize_text_field(
904
				sprintf( '%s: %s', $sso_redirect->get_error_code(), $sso_redirect->get_error_message() )
0 ignored issues
show
Bug introduced by
The method get_error_code cannot be called on $sso_redirect (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The method get_error_message cannot be called on $sso_redirect (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
905
			);
906
			JetpackTracking::record_user_event( 'sso_login_redirect_failed', array(
907
				'error_message' => $error_message
908
			) );
909
			wp_die( $error_message );
910
		}
911
912
		return $sso_redirect;
913
	}
914
915
	/**
916
	 * Build WordPress.com SSO URL with appropriate query parameters.
917
	 *
918
	 * @param  array  $args Optional query parameters.
919
	 * @return string       WordPress.com SSO URL
920
	 */
921
	function build_sso_url( $args = array() ) {
922
		$sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
923
		$defaults = array(
924
			'action'       => 'jetpack-sso',
925
			'site_id'      => Jetpack_Options::get_option( 'id' ),
926
			'sso_nonce'    => $sso_nonce,
927
			'calypso_auth' => '1',
928
		);
929
930
		$args = wp_parse_args( $args, $defaults );
931
932
		if ( is_wp_error( $args['sso_nonce'] ) ) {
933
			return $args['sso_nonce'];
934
		}
935
936
		return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
937
	}
938
939
	/**
940
	 * Build WordPress.com SSO URL with appropriate query parameters,
941
	 * including the parameters necessary to force the user to reauthenticate
942
	 * on WordPress.com.
943
	 *
944
	 * @param  array  $args Optional query parameters.
945
	 * @return string       WordPress.com SSO URL
946
	 */
947
	function build_reauth_and_sso_url( $args = array() ) {
948
		$sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
949
		$redirect = $this->build_sso_url( array( 'force_auth' => '1', 'sso_nonce' => $sso_nonce ) );
950
951
		if ( is_wp_error( $redirect ) ) {
952
			return $redirect;
953
		}
954
955
		$defaults = array(
956
			'action'       => 'jetpack-sso',
957
			'site_id'      => Jetpack_Options::get_option( 'id' ),
958
			'sso_nonce'    => $sso_nonce,
959
			'reauth'       => '1',
960
			'redirect_to'  => urlencode( $redirect ),
961
			'calypso_auth' => '1',
962
		);
963
964
		$args = wp_parse_args( $args, $defaults );
965
966
		if ( is_wp_error( $args['sso_nonce'] ) ) {
967
			return $args['sso_nonce'];
968
		}
969
970
		return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
971
	}
972
973
	/**
974
	 * Determines local user associated with a given WordPress.com user ID.
975
	 *
976
	 * @since 2.6.0
977
	 *
978
	 * @param int $wpcom_user_id User ID from WordPress.com
979
	 * @return object Local user object if found, null if not.
980
	 */
981
	static function get_user_by_wpcom_id( $wpcom_user_id ) {
982
		$user_query = new WP_User_Query( array(
983
			'meta_key'   => 'wpcom_user_id',
984
			'meta_value' => intval( $wpcom_user_id ),
985
			'number'     => 1,
986
		) );
987
988
		$users = $user_query->get_results();
989
		return $users ? array_shift( $users ) : null;
990
	}
991
992
	/**
993
	 * Error message displayed on the login form when two step is required and
994
	 * the user's account on WordPress.com does not have two step enabled.
995
	 *
996
	 * @since 2.7
997
	 * @param string $message
998
	 * @return string
999
	 **/
1000
	public function error_msg_enable_two_step( $message ) {
1001
		$error = sprintf(
1002
			wp_kses(
1003
				__(
1004
					'Two-Step Authentication is required to access this site. Please visit your <a href="%1$s" target="_blank">Security Settings</a> to configure <a href="%2$S" target="_blank">Two-step Authentication</a> for your account.',
1005
					'jetpack'
1006
				),
1007
				array(  'a' => array( 'href' => array() ) )
1008
			),
1009
			'https://wordpress.com/me/security/two-step',
1010
			'https://support.wordpress.com/security/two-step-authentication/'
1011
		);
1012
1013
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
1014
1015
		return $message;
1016
	}
1017
1018
	/**
1019
	 * Error message displayed when the user tries to SSO, but match by email
1020
	 * is off and they already have an account with their email address on
1021
	 * this site.
1022
	 *
1023
	 * @param string $message
1024
	 * @return string
1025
	 */
1026
	public function error_msg_email_already_exists( $message ) {
1027
		$error = sprintf(
1028
			wp_kses(
1029
				__(
1030
					'You already have an account on this site. Please <a href="%1$s">sign in</a> with your username and password and then connect to WordPress.com.',
1031
					'jetpack'
1032
				),
1033
				array(  'a' => array( 'href' => array() ) )
1034
			),
1035
			esc_url_raw( add_query_arg( 'jetpack-sso-show-default-form', '1', wp_login_url() ) )
1036
		);
1037
1038
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
1039
1040
		return $message;
1041
	}
1042
1043
	/**
1044
	 * Builds the translation ready string that is to be used when the site hides the default login form.
1045
	 *
1046
	 * @since 4.1.0
1047
	 * @return string
1048
	 */
1049
	public function get_sso_required_message() {
1050
		$msg = esc_html__( 'A WordPress.com account is required to access this site. Click the button below to sign in or create a free WordPress.com account.', 'jetpack' );
1051
1052
		/**
1053
		 * Filter the message displayed when the default WordPress login form is disabled.
1054
		 *
1055
		 * @module sso
1056
		 *
1057
		 * @since 2.8.0
1058
		 *
1059
		 * @param string $msg Disclaimer when default WordPress login form is disabled.
1060
		 */
1061
		return apply_filters( 'jetpack_sso_disclaimer_message', $msg );
1062
	}
1063
1064
	/**
1065
	 * Message displayed when the site admin has disabled the default WordPress
1066
	 * login form in Settings > General > Single Sign On
1067
	 *
1068
	 * @since 2.7
1069
	 * @param string $message
1070
	 *
1071
	 * @return string
1072
	 **/
1073
	public function msg_login_by_jetpack( $message ) {
1074
		$msg = $this->get_sso_required_message();
1075
1076
		if ( empty( $msg ) ) {
1077
			return $message;
1078
		}
1079
1080
		$message .= sprintf( '<p class="message">%s</p>', $msg );
1081
		return $message;
1082
	}
1083
1084
	/**
1085
	 * Message displayed when the user can not be found after approving the SSO process on WordPress.com
1086
	 *
1087
	 * @param string $message
1088
	 * @return string
1089
	 */
1090
	function cant_find_user( $message ) {
1091
		$error = esc_html__(
1092
			"We couldn't find your account. If you already have an account, make sure you have connected to WordPress.com.",
1093
			'jetpack'
1094
		);
1095
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
1096
1097
		return $message;
1098
	}
1099
1100
	/**
1101
	 * When jetpack-sso-auth-redirect query parameter is set, will redirect user to
1102
	 * WordPress.com authorization flow.
1103
	 *
1104
	 * We redirect here instead of in handle_login() because Jetpack::init()->build_connect_url
1105
	 * calls menu_page_url() which doesn't work properly until admin menus are registered.
1106
	 */
1107
	function maybe_authorize_user_after_sso() {
1108
		if ( empty( $_GET['jetpack-sso-auth-redirect'] ) ) {
1109
			return;
1110
		}
1111
1112
		$redirect_to = ! empty( $_GET['redirect_to'] ) ? esc_url_raw( $_GET['redirect_to'] ) : admin_url();
1113
		$request_redirect_to = ! empty( $_GET['request_redirect_to'] ) ? esc_url_raw( $_GET['request_redirect_to'] ) : $redirect_to;
1114
1115
		/** This filter is documented in core/src/wp-login.php */
1116
		$redirect_after_auth = apply_filters( 'login_redirect', $redirect_to, $request_redirect_to, wp_get_current_user() );
1117
1118
		/**
1119
		 * Since we are passing this redirect to WordPress.com and therefore can not use wp_safe_redirect(),
1120
		 * let's sanitize it here to make sure it's safe. If the redirect is not safe, then use admin_url().
1121
		 */
1122
		$redirect_after_auth = wp_sanitize_redirect( $redirect_after_auth );
1123
		$redirect_after_auth = wp_validate_redirect( $redirect_after_auth, admin_url() );
1124
1125
		/**
1126
		 * Return the raw connect URL with our redirect and attribute connection to SSO.
1127
		 */
1128
		$connect_url = Jetpack::init()->build_connect_url( true, $redirect_after_auth, 'sso' );
1129
1130
		add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
1131
		wp_safe_redirect( $connect_url );
1132
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method maybe_authorize_user_after_sso() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1133
	}
1134
1135
	/**
1136
	 * Cache user's display name and Gravatar so it can be displayed on the login screen. These cookies are
1137
	 * stored when the user logs out, and then deleted when the user logs in.
1138
	 */
1139
	function store_wpcom_profile_cookies_on_logout() {
1140
		if ( ! Jetpack::is_user_connected( get_current_user_id() ) ) {
1141
			return;
1142
		}
1143
1144
		$user_data = $this->get_user_data( get_current_user_id() );
1145
		if ( ! $user_data ) {
1146
			return;
1147
		}
1148
1149
		setcookie(
1150
			'jetpack_sso_wpcom_name_' . COOKIEHASH,
1151
			$user_data->display_name,
1152
			time() + WEEK_IN_SECONDS,
1153
			COOKIEPATH,
1154
			COOKIE_DOMAIN
1155
		);
1156
1157
		setcookie(
1158
			'jetpack_sso_wpcom_gravatar_' . COOKIEHASH,
1159
			get_avatar_url(
1160
				$user_data->email,
1161
				array( 'size' => 72, 'default' => 'mystery' )
1162
			),
1163
			time() + WEEK_IN_SECONDS,
1164
			COOKIEPATH,
1165
			COOKIE_DOMAIN
1166
		);
1167
	}
1168
1169
	/**
1170
	 * Determines if a local user is connected to WordPress.com
1171
	 *
1172
	 * @since 2.8
1173
	 * @param integer $user_id - Local user id
1174
	 * @return boolean
1175
	 **/
1176
	public function is_user_connected( $user_id ) {
1177
		return $this->get_user_data( $user_id );
1178
	}
1179
1180
	/**
1181
	 * Retrieves a user's WordPress.com data
1182
	 *
1183
	 * @since 2.8
1184
	 * @param integer $user_id - Local user id
1185
	 * @return mixed null or stdClass
1186
	 **/
1187
	public function get_user_data( $user_id ) {
1188
		return get_user_meta( $user_id, 'wpcom_user_data', true );
1189
	}
1190
}
1191
1192
Jetpack_SSO::get_instance();
1193