Completed
Push — add/fallback-views ( 111d6f...097491 )
by
unknown
21:40 queued 11:40
created

Jetpack_SSO::edit_profile_fields()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 43
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 28
nc 4
nop 1
dl 0
loc 43
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Module Name: Single Sign On
5
 * Module Description: Secure user authentication with WordPress.com.
6
 * Jumpstart Description: Lets you log in to all your Jetpack-enabled sites with one click using your WordPress.com account.
7
 * Sort Order: 30
8
 * Recommendation Order: 5
9
 * First Introduced: 2.6
10
 * Requires Connection: Yes
11
 * Auto Activate: No
12
 * Module Tags: Developers
13
 * Feature: Security, Jumpstart
14
 * Additional Search Queries: sso, single sign on, login, log in
15
 */
16
17
class Jetpack_SSO {
18
	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...
19
20
	private function __construct() {
21
22
		self::$instance = $this;
23
24
		add_action( 'admin_init',             array( $this, 'maybe_authorize_user_after_sso' ), 1 );
25
		add_action( 'admin_init',             array( $this, 'register_settings' ) );
26
		add_action( 'login_init',             array( $this, 'login_init' ) );
27
		add_action( 'delete_user',            array( $this, 'delete_connection_for_user' ) );
28
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
29
		add_action( 'init',                   array( $this, 'maybe_logout_user' ), 5 );
30
		add_action( 'jetpack_modules_loaded', array( $this, 'module_configure_button' ) );
31
		add_action( 'admin_enqueue_scripts',  array( $this, 'admin_enqueue_scripts' ) );
32
		add_action( 'login_form_logout',      array( $this, 'store_wpcom_profile_cookies_on_logout' ) );
33
		add_action( 'wp_login',               array( 'Jetpack_SSO', 'clear_wpcom_profile_cookies' ) );
34
		add_action( 'jetpack_unlinked_user',  array( $this, 'delete_connection_for_user') );
35
36
		// Adding this action so that on login_init, the action won't be sanitized out of the $action global.
37
		add_action( 'login_form_jetpack-sso', '__return_true' );
38
39
		if (
40
			$this->should_hide_login_form() &&
41
			/**
42
			 * Filter the display of the disclaimer message appearing when default WordPress login form is disabled.
43
			 *
44
			 * @module sso
45
			 *
46
			 * @since 2.8.0
47
			 *
48
			 * @param bool true Should the disclaimer be displayed. Default to true.
49
			 */
50
			apply_filters( 'jetpack_sso_display_disclaimer', true )
51
		) {
52
			add_filter( 'login_message', array( $this, 'msg_login_by_jetpack' ) );
53
		}
54
	}
55
56
	/**
57
	 * Returns the single instance of the Jetpack_SSO object
58
	 *
59
	 * @since 2.8
60
	 * @return Jetpack_SSO
61
	 **/
62
	public static function get_instance() {
63
		if ( ! is_null( self::$instance ) ) {
64
			return self::$instance;
65
		}
66
67
		return self::$instance = new Jetpack_SSO;
68
	}
69
70
	/**
71
	 * Add configure button and functionality to the module card on the Jetpack screen
72
	 **/
73
	public static function module_configure_button() {
74
		Jetpack::enable_module_configurable( __FILE__ );
75
		Jetpack::module_configuration_load( __FILE__, array( __CLASS__, 'module_configuration_load' ) );
76
		Jetpack::module_configuration_head( __FILE__, array( __CLASS__, 'module_configuration_head' ) );
77
		Jetpack::module_configuration_screen( __FILE__, array( __CLASS__, 'module_configuration_screen' ) );
78
	}
79
80
	public static function module_configuration_load() {}
81
82
	public static function module_configuration_head() {}
83
84
	public static function module_configuration_screen() {
85
		?>
86
		<form method="post" action="options.php">
87
			<?php settings_fields( 'jetpack-sso' ); ?>
88
			<?php do_settings_sections( 'jetpack-sso' ); ?>
89
			<?php submit_button(); ?>
90
		</form>
91
		<?php
92
	}
93
94
	/**
95
	 * If jetpack_force_logout == 1 in current user meta the user will be forced
96
	 * to logout and reauthenticate with the site.
97
	 **/
98
	public function maybe_logout_user() {
99
		global $current_user;
100
101
		if ( 1 == $current_user->jetpack_force_logout ) {
102
			delete_user_meta( $current_user->ID, 'jetpack_force_logout' );
103
			self::delete_connection_for_user( $current_user->ID );
104
			wp_logout();
105
			wp_safe_redirect( wp_login_url() );
106
			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...
107
		}
108
	}
109
110
111
	/**
112
	 * Adds additional methods the WordPress xmlrpc API for handling SSO specific features
113
	 *
114
	 * @param array $methods
115
	 * @return array
116
	 **/
117
	public function xmlrpc_methods( $methods ) {
118
		$methods['jetpack.userDisconnect'] = array( $this, 'xmlrpc_user_disconnect' );
119
		return $methods;
120
	}
121
122
	/**
123
	 * Marks a user's profile for disconnect from WordPress.com and forces a logout
124
	 * the next time the user visits the site.
125
	 **/
126
	public function xmlrpc_user_disconnect( $user_id ) {
127
		$user_query = new WP_User_Query(
128
			array(
129
				'meta_key' => 'wpcom_user_id',
130
				'meta_value' => $user_id,
131
			)
132
		);
133
		$user = $user_query->get_results();
134
		$user = $user[0];
135
136
		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...
137
			$user = wp_set_current_user( $user->ID );
138
			update_user_meta( $user->ID, 'jetpack_force_logout', '1' );
139
			self::delete_connection_for_user( $user->ID );
140
			return true;
141
		}
142
		return false;
143
	}
144
145
	/**
146
	 * Enqueues scripts and styles necessary for SSO login.
147
	 */
148
	public function login_enqueue_scripts() {
149
		global $action;
150
151
		if ( ! in_array( $action, array( 'jetpack-sso', 'login' ) ) ) {
152
			return;
153
		}
154
155
		wp_enqueue_style( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.css', JETPACK__PLUGIN_FILE ), array( 'login', 'genericons' ), JETPACK__VERSION );
156
		wp_enqueue_script( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.js', JETPACK__PLUGIN_FILE ), array( 'jquery' ), JETPACK__VERSION );
157
	}
158
159
	/**
160
	 * Enqueue styles neceessary for Jetpack SSO on users' profiles
161
	 */
162
	public function admin_enqueue_scripts() {
163
		$screen = get_current_screen();
164
165
		if ( empty( $screen ) || ! in_array( $screen->base, array( 'edit-user', 'profile' ) ) ) {
166
			return;
167
		}
168
169
		wp_enqueue_style( 'jetpack-sso-profile', plugins_url( 'modules/sso/jetpack-sso-profile.css', JETPACK__PLUGIN_FILE ), array( 'genericons' ), JETPACK__VERSION );
170
	}
171
172
	/**
173
	 * Adds Jetpack SSO classes to login body
174
	 *
175
	 * @param  array $classes Array of classes to add to body tag
176
	 * @return array          Array of classes to add to body tag
177
	 */
178
	public function login_body_class( $classes ) {
179
		global $action;
180
181
		if ( ! in_array( $action, array( 'jetpack-sso', 'login' ) ) ) {
182
			return $classes;
183
		}
184
185
		// If jetpack-sso-default-form, show the default login form.
186
		if ( isset( $_GET['jetpack-sso-default-form'] ) && 1 == $_GET['jetpack-sso-default-form'] ) {
187
			return $classes;
188
		}
189
190
		$classes[] = 'jetpack-sso-body';
191
		return $classes;
192
	}
193
194
	/**
195
	 * Adds settings fields to Settings > General > Single Sign On that allows users to
196
	 * turn off the login form on wp-login.php
197
	 *
198
	 * @since 2.7
199
	 **/
200
	public function register_settings() {
201
202
		add_settings_section(
203
			'jetpack_sso_settings',
204
			__( 'Single Sign On' , 'jetpack' ),
205
			'__return_false',
206
			'jetpack-sso'
207
		);
208
209
		/*
210
		 * Settings > General > Single Sign On
211
		 * Require two step authentication
212
		 */
213
		register_setting(
214
			'jetpack-sso',
215
			'jetpack_sso_require_two_step',
216
			array( $this, 'validate_jetpack_sso_require_two_step' )
217
		);
218
219
		add_settings_field(
220
			'jetpack_sso_require_two_step',
221
			'', // __( 'Require Two-Step Authentication' , 'jetpack' ),
222
			array( $this, 'render_require_two_step' ),
223
			'jetpack-sso',
224
			'jetpack_sso_settings'
225
		);
226
227
		/*
228
		 * Settings > General > Single Sign On
229
		 */
230
		register_setting(
231
			'jetpack-sso',
232
			'jetpack_sso_match_by_email',
233
			array( $this, 'validate_jetpack_sso_match_by_email' )
234
		);
235
236
		add_settings_field(
237
			'jetpack_sso_match_by_email',
238
			'', // __( 'Match by Email' , 'jetpack' ),
239
			array( $this, 'render_match_by_email' ),
240
			'jetpack-sso',
241
			'jetpack_sso_settings'
242
		);
243
	}
244
245
	/**
246
	 * Builds the display for the checkbox allowing user to require two step
247
	 * auth be enabled on WordPress.com accounts before login. Displays in Settings > General
248
	 *
249
	 * @since 2.7
250
	 **/
251
	public function render_require_two_step() {
252
		/** This filter is documented in modules/sso.php */
253
		$require_two_step = ( 1 == apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step' ) ) );
254
		$disabled = $require_two_step ? ' disabled="disabled"' : '';
255
		echo '<label>';
256
		echo '<input type="checkbox" name="jetpack_sso_require_two_step" ' . checked( $require_two_step, true, false ) . "$disabled>";
257
		esc_html_e( 'Require Two-Step Authentication' , 'jetpack' );
258
		echo '</label>';
259
	}
260
261
	/**
262
	 * Validate the require  two step checkbox in Settings > General
263
	 *
264
	 * @since 2.7
265
	 * @return boolean
266
	 **/
267
	public function validate_jetpack_sso_require_two_step( $input ) {
268
		return ( ! empty( $input ) ) ? 1 : 0;
269
	}
270
271
	/**
272
	 * Builds the display for the checkbox allowing the user to allow matching logins by email
273
	 * Displays in Settings > General
274
	 *
275
	 * @since 2.9
276
	 **/
277
	public function render_match_by_email() {
278
		$match_by_email = 1 == $this->match_by_email();
279
		$disabled = $match_by_email ? ' disabled="disabled"' : '';
280
		echo '<label>';
281
		echo '<input type="checkbox" name="jetpack_sso_match_by_email"' . checked( $match_by_email, true, false ) . "$disabled>";
282
		esc_html_e( 'Match by Email', 'jetpack' );
283
		echo '</label>';
284
	}
285
286
	/**
287
	 * Validate the match by email check in Settings > General
288
	 *
289
	 * @since 2.9
290
	 * @return boolean
291
	 **/
292
	public function validate_jetpack_sso_match_by_email( $input ) {
293
		return ( ! empty( $input ) ) ? 1 : 0;
294
	}
295
296
	/**
297
	 * Checks to determine if the user wants to login on wp-login
298
	 *
299
	 * This function mostly exists to cover the exceptions to login
300
	 * that may exist as other parameters to $_GET[action] as $_GET[action]
301
	 * does not have to exist. By default WordPress assumes login if an action
302
	 * is not set, however this may not be true, as in the case of logout
303
	 * where $_GET[loggedout] is instead set
304
	 *
305
	 * @return boolean
306
	 **/
307
	private function wants_to_login() {
308
		$wants_to_login = false;
309
310
		// Cover default WordPress behavior
311
		$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'login';
312
313
		// And now the exceptions
314
		$action = isset( $_GET['loggedout'] ) ? 'loggedout' : $action;
315
316
		if ( 'login' == $action ) {
317
			$wants_to_login = true;
318
		}
319
320
		return $wants_to_login;
321
	}
322
323
	private function bypass_login_forward_wpcom() {
324
		/**
325
		 * Redirect the site's log in form to WordPress.com's log in form.
326
		 *
327
		 * @module sso
328
		 *
329
		 * @since 3.1.0
330
		 *
331
		 * @param bool false Should the site's log in form be automatically forwarded to WordPress.com's log in form.
332
		 */
333
		return apply_filters( 'jetpack_sso_bypass_login_forward_wpcom', false );
334
	}
335
336
	function login_init() {
337
		global $action;
338
339
		/**
340
		 * If the user is attempting to logout AND the auto-forward to WordPress.com
341
		 * login is set then we need to ensure we do not auto-forward the user and get
342
		 * them stuck in an infinite logout loop.
343
		 */
344
		if ( isset( $_GET['loggedout'] ) && $this->bypass_login_forward_wpcom() ) {
345
			add_filter( 'jetpack_remove_login_form', '__return_true' );
346
		}
347
348
		/**
349
		 * Check to see if the site admin wants to automagically forward the user
350
		 * to the WordPress.com login page AND  that the request to wp-login.php
351
		 * is not something other than login (Like logout!)
352
		 */
353
		if (
354
			$this->wants_to_login()
355
			&& $this->bypass_login_forward_wpcom()
356
		) {
357
			add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
358
			$this->maybe_save_cookie_redirect();
359
			$reauth = ! empty( $_GET['force_reauth'] );
360
			$sso_url = $this->get_sso_url_or_die( $reauth );
361
			JetpackTracking::record_user_event( 'sso_login_redirect_bypass_success' );
362
			wp_safe_redirect( $sso_url );
363
			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...
364
		}
365
366
		if ( 'login' === $action ) {
367
			$this->display_sso_login_form();
368
		} elseif ( 'jetpack-sso' === $action ) {
369
			if ( isset( $_GET['result'], $_GET['user_id'], $_GET['sso_nonce'] ) && 'success' == $_GET['result'] ) {
370
				$this->handle_login();
371
				$this->display_sso_login_form();
372
			} else {
373
				if ( Jetpack::check_identity_crisis() ) {
374
					JetpackTracking::record_user_event( 'sso_login_redirect_failed', array(
375
						'error_message' => 'identity_crisis'
376
					) );
377
					wp_die( __( "Error: This site's Jetpack connection is currently experiencing problems.", 'jetpack' ) );
378
				} else {
379
					$this->maybe_save_cookie_redirect();
380
					// Is it wiser to just use wp_redirect than do this runaround to wp_safe_redirect?
381
					add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
382
					$reauth = ! empty( $_GET['force_reauth'] );
383
					$sso_url = $this->get_sso_url_or_die( $reauth );
384
					JetpackTracking::record_user_event( 'sso_login_redirect_success' );
385
					wp_safe_redirect( $sso_url );
386
					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...
387
				}
388
			}
389
		}
390
	}
391
392
	/**
393
	 * Ensures that we can get a nonce from WordPress.com via XML-RPC before setting
394
	 * up the hooks required to display the SSO form.
395
	 */
396
	public function display_sso_login_form() {
397
		$sso_nonce = self::request_initial_nonce();
398
		if ( is_wp_error( $sso_nonce ) ) {
399
			return;
400
		}
401
402
		add_action( 'login_form',            array( $this, 'login_form' ) );
403
		add_filter( 'login_body_class',      array( $this, 'login_body_class' ) );
404
		add_action( 'login_enqueue_scripts', array( $this, 'login_enqueue_scripts' ) );
405
	}
406
407
	/**
408
	 * Conditionally save the redirect_to url as a cookie.
409
	 */
410
	public static function maybe_save_cookie_redirect() {
411
		if ( headers_sent() ) {
412
			return new WP_Error( 'headers_sent', __( 'Cannot deal with cookie redirects, as headers are already sent.', 'jetpack' ) );
413
		}
414
415
		if ( ! empty( $_GET['redirect_to'] ) ) {
416
			// If we have something to redirect to
417
			$url = esc_url_raw( $_GET['redirect_to'] );
418
			setcookie( 'jetpack_sso_redirect_to', $url, time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
419
420
		} elseif ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
421
			// Otherwise, if it's already set, purge it.
422
			setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
423
		}
424
425
		if ( ! empty( $_GET['rememberme'] ) ) {
426
			setcookie( 'jetpack_sso_remember_me', '1', time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
427
		} elseif ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
428
			setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
429
		}
430
	}
431
432
	/**
433
	 * Determine if the login form should be hidden or not
434
	 *
435
	 * Method is private only because it is only used in this class so far.
436
	 * Feel free to change it later
437
	 *
438
	 * @return bool
439
	 **/
440
	private function should_hide_login_form() {
441
		/**
442
		 * Remove the default log in form, only leave the WordPress.com log in button.
443
		 *
444
		 * @module sso
445
		 *
446
		 * @since 3.1.0
447
		 *
448
		 * @param bool get_option( 'jetpack_sso_remove_login_form', false ) Should the default log in form be removed. Default to false.
449
		 */
450
		return apply_filters( 'jetpack_remove_login_form', get_option( 'jetpack_sso_remove_login_form', false ) );
451
	}
452
453
	/**
454
	 * Outputs the Jetpack SSO button and description as well as the toggle link
455
	 * for switching between Jetpack SSO and default login.
456
	 */
457
	function login_form() {
458
		$site_name = get_bloginfo( 'name' );
459
		if ( ! $site_name ) {
460
			$site_name = get_bloginfo( 'url' );
461
		}
462
463
		$display_name = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] )
464
			? $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ]
465
			: false;
466
		$gravatar = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] )
467
			? $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ]
468
			: false;
469
470
		?>
471
		<div id="jetpack-sso-wrap">
472
			<?php if ( $display_name && $gravatar ) : ?>
473
				<div id="jetpack-sso-wrap__user">
474
					<img width="72" height="72" src="<?php echo esc_html( $gravatar ); ?>" />
475
476
					<h2>
477
						<?php
478
							echo wp_kses(
479
								sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
480
								array( 'span' => true )
481
							);
482
						?>
483
					</h2>
484
				</div>
485
486
			<?php endif; ?>
487
488
489
			<div id="jetpack-sso-wrap__action">
490
				<?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...
491
492
				<?php if ( $display_name && $gravatar ) : ?>
493
					<a class="jetpack-sso-wrap__reauth" href="<?php echo esc_url( $this->build_sso_button_url( array( 'force_reauth' => '1' ) ) ); ?>">
494
						<?php esc_html_e( 'Log in as a different WordPress.com user', 'jetpack' ); ?>
495
					</a>
496
				<?php else : ?>
497
					<p>
498
						<?php
499
							echo esc_html(
500
								sprintf(
501
									__( 'You can now save time spent logging in by connecting your WordPress.com account to %s.', 'jetpack' ),
502
									esc_html( $site_name )
503
								)
504
							);
505
						?>
506
					</p>
507
				<?php endif; ?>
508
			</div>
509
510
			<?php if ( ! $this->should_hide_login_form() ) : ?>
511
				<div class="jetpack-sso-or">
512
					<span><?php esc_html_e( 'Or', 'jetpack' ); ?></span>
513
				</div>
514
515
				<a href="<?php echo add_query_arg( 'jetpack-sso-default-form', '1' ); ?>" class="jetpack-sso-toggle wpcom">
516
					<?php
517
						esc_html_e( 'Log in with username and password', 'jetpack' )
518
					?>
519
				</a>
520
521
				<a href="<?php echo add_query_arg( 'jetpack-sso-default-form', '0' ); ?>" class="jetpack-sso-toggle default">
522
					<?php
523
						esc_html_e( 'Log in with WordPress.com', 'jetpack' )
524
					?>
525
				</a>
526
			<?php endif; ?>
527
		</div>
528
		<?php
529
	}
530
531
	/**
532
	 * Clear the cookies that store the profile information for the last
533
	 * WPCOM user to connect.
534
	 */
535
	static function clear_wpcom_profile_cookies() {
536 View Code Duplication
		if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) ) {
537
			setcookie(
538
				'jetpack_sso_wpcom_name_' . COOKIEHASH,
539
				' ',
540
				time() - YEAR_IN_SECONDS,
541
				COOKIEPATH,
542
				COOKIE_DOMAIN
543
			);
544
		}
545
546 View Code Duplication
		if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) ) {
547
			setcookie(
548
				'jetpack_sso_wpcom_gravatar_' . COOKIEHASH,
549
				' ',
550
				time() - YEAR_IN_SECONDS,
551
				COOKIEPATH,
552
				COOKIE_DOMAIN
553
			);
554
		}
555
	}
556
557
	static function delete_connection_for_user( $user_id ) {
558
		if ( ! $wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true ) ) {
559
			return;
560
		}
561
		Jetpack::load_xml_rpc_client();
562
		$xml = new Jetpack_IXR_Client( array(
563
			'wpcom_user_id' => $user_id,
564
		) );
565
		$xml->query( 'jetpack.sso.removeUser', $wpcom_user_id );
566
567
		if ( $xml->isError() ) {
568
			return false;
569
		}
570
571
		// Clean up local data stored for SSO
572
		delete_user_meta( $user_id, 'wpcom_user_id' );
573
		delete_user_meta( $user_id, 'wpcom_user_data'  );
574
		self::clear_wpcom_profile_cookies();
575
576
		return $xml->getResponse();
577
	}
578
579 View Code Duplication
	static function request_initial_nonce() {
580
		Jetpack::load_xml_rpc_client();
581
		$xml = new Jetpack_IXR_Client( array(
582
			'user_id' => get_current_user_id(),
583
		) );
584
		$xml->query( 'jetpack.sso.requestNonce' );
585
586
		if ( $xml->isError() ) {
587
			return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() );
588
		}
589
590
		return $xml->getResponse();
591
	}
592
593
	/**
594
	 * The function that actually handles the login!
595
	 */
596
	function handle_login() {
597
		$wpcom_nonce   = sanitize_key( $_GET['sso_nonce'] );
598
		$wpcom_user_id = (int) $_GET['user_id'];
599
600
		Jetpack::load_xml_rpc_client();
601
		$xml = new Jetpack_IXR_Client( array(
602
			'user_id' => get_current_user_id(),
603
		) );
604
		$xml->query( 'jetpack.sso.validateResult', $wpcom_nonce, $wpcom_user_id );
605
606
		if ( $xml->isError() ) {
607
			$error_message = sanitize_text_field(
608
				sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() )
609
			);
610
			JetpackTracking::record_user_event( 'sso_login_failed', array(
611
				'error_message' => $error_message
612
			) );
613
			wp_die( $error_message );
614
		}
615
616
		$user_data = $xml->getResponse();
617
618
		if ( empty( $user_data ) ) {
619
			JetpackTracking::record_user_event( 'sso_login_failed', array(
620
				'error_message' => 'invalid_response_data'
621
			) );
622
			wp_die( __( 'Error, invalid response data.', 'jetpack' ) );
623
		}
624
625
		$user_data = (object) $user_data;
626
		$user = null;
627
628
		/**
629
		 * Fires before Jetpack's SSO modifies the log in form.
630
		 *
631
		 * @module sso
632
		 *
633
		 * @since 2.6.0
634
		 *
635
		 * @param object $user_data User login information.
636
		 */
637
		do_action( 'jetpack_sso_pre_handle_login', $user_data );
638
639
		/**
640
		 * Is it required to have 2-step authentication enabled on WordPress.com to use SSO?
641
		 *
642
		 * @module sso
643
		 *
644
		 * @since 2.8.0
645
		 *
646
		 * @param bool get_option( 'jetpack_sso_require_two_step' ) Does SSO require 2-step authentication?
647
		 */
648
		$require_two_step = apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step' ) );
649
		if ( $require_two_step && 0 == (int) $user_data->two_step_enabled ) {
650
			$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...
651
652
			JetpackTracking::record_user_event( 'sso_login_failed', array(
653
				'error_message' => 'error_msg_enable_two_step'
654
			) );
655
			
656
			/** This filter is documented in core/src/wp-includes/pluggable.php */
657
			do_action( 'wp_login_failed', $user_data->login );
658
			add_filter( 'login_message', array( $this, 'error_msg_enable_two_step' ) );
659
			return;
660
		}
661
662
		$user_found_with = '';
663
		if ( empty( $user ) && isset( $user_data->external_user_id ) ) {
664
			$user_found_with = 'external_user_id';
665
			$user = get_user_by( 'id', intval( $user_data->external_user_id ) );
666
			if ( $user ) {
667
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
668
			}
669
		}
670
671
		// If we don't have one by wpcom_user_id, try by the email?
672
		if ( empty( $user ) && self::match_by_email() ) {
673
			$user_found_with = 'match_by_email';
674
			$user = get_user_by( 'email', $user_data->email );
675
			if ( $user ) {
676
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
677
			}
678
		}
679
680
		// If we've still got nothing, create the user.
681
		if ( empty( $user ) && ( get_option( 'users_can_register' ) || self::new_user_override() ) ) {
682
			// If not matching by email we still need to verify the email does not exist
683
			// or this blows up
684
			/**
685
			 * If match_by_email is true, we know the email doesn't exist, as it would have
686
			 * been found in the first pass.  If get_user_by( 'email' ) doesn't find the
687
			 * user, then we know that email is unused, so it's safe to add.
688
			 */
689
			if ( self::match_by_email() || ! get_user_by( 'email', $user_data->email ) ) {
690
				$username = $user_data->login;
691
692
				if ( username_exists( $username ) ) {
693
					$username = $user_data->login . '_' . $user_data->ID;
694
				}
695
696
				$tries = 0;
697
				while ( username_exists( $username ) ) {
698
					$username = $user_data->login . '_' . $user_data->ID . '_' . mt_rand();
699
					if ( $tries++ >= 5 ) {
700
						JetpackTracking::record_user_event( 'sso_login_failed', array(
701
							'error_message' => 'could_not_create_username'
702
						) );
703
						wp_die( __( "Error: Couldn't create suitable username.", 'jetpack' ) );
704
					}
705
				}
706
707
				$user_found_with = self::new_user_override()
708
					? 'user_created_new_user_override'
709
					: 'user_created_users_can_register';
710
711
				$password = wp_generate_password( 20 );
712
				$user_id  = wp_create_user( $username, $password, $user_data->email );
713
				$user     = get_userdata( $user_id );
714
715
				$user->display_name = $user_data->display_name;
716
				$user->first_name   = $user_data->first_name;
717
				$user->last_name    = $user_data->last_name;
718
				$user->url          = $user_data->url;
719
				$user->description  = $user_data->description;
720
				wp_update_user( $user );
721
722
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
723
			} else {
724
				JetpackTracking::record_user_event( 'sso_login_failed', array(
725
					'error_message' => 'error_msg_email_already_exists'
726
				) );
727
728
				$this->user_data = $user_data;
729
				add_action( 'login_message', array( $this, 'error_msg_email_already_exists' ) );
730
				return;
731
			}
732
		}
733
734
		/**
735
		 * Fires after we got login information from WordPress.com.
736
		 *
737
		 * @module sso
738
		 *
739
		 * @since 2.6.0
740
		 *
741
		 * @param array $user WordPress.com User information.
742
		 * @param object $user_data User Login information.
743
		 */
744
		do_action( 'jetpack_sso_handle_login', $user, $user_data );
745
746
		if ( $user ) {
747
			// Cache the user's details, so we can present it back to them on their user screen
748
			update_user_meta( $user->ID, 'wpcom_user_data', $user_data );
749
750
			$remember = false;
751 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
752
				$remember = true;
753
				// And then purge it
754
				setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
755
			}
756
			/**
757
			 * Filter the remember me value.
758
			 *
759
			 * @module sso
760
			 *
761
			 * @since 2.8.0
762
			 *
763
			 * @param bool $remember Is the remember me option checked?
764
			 */
765
			$remember = apply_filters( 'jetpack_remember_login', $remember );
766
			wp_set_auth_cookie( $user->ID, $remember );
767
768
			/** This filter is documented in core/src/wp-includes/user.php */
769
			do_action( 'wp_login', $user->user_login, $user );
770
771
			wp_set_current_user( $user->ID );
772
773
			$_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( $_REQUEST['redirect_to'] ) : '';
774
			$redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : self::profile_page_url();
775
776
			// If we have a saved redirect to request in a cookie
777 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
778
				// Set that as the requested redirect to
779
				$redirect_to = $_request_redirect_to = esc_url_raw( $_COOKIE['jetpack_sso_redirect_to'] );
780
				// And then purge it
781
				setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
782
			}
783
784
			$is_user_connected = Jetpack::is_user_connected( $user->ID );
785
			JetpackTracking::record_user_event( 'sso_user_logged_in', array(
786
				'user_found_with' => $user_found_with,
787
				'user_connected'  => (bool) $is_user_connected,
788
				'user_role'       => Jetpack::init()->translate_current_user_to_role()
789
			) );
790
791
			if ( ! $is_user_connected ) {
792
				$calypso_env = ! empty( $_GET['calypso_env'] )
793
					? sanitize_key( $_GET['calypso_env'] )
794
					: '';
795
796
				wp_safe_redirect(
797
					add_query_arg(
798
						array(
799
							'redirect_to'               => $redirect_to,
800
							'request_redirect_to'       => $_request_redirect_to,
801
							'calypso_env'               => $calypso_env,
802
							'jetpack-sso-auth-redirect' => '1',
803
						),
804
						admin_url()
805
					)
806
				);
807
				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...
808
			}
809
810
			wp_safe_redirect(
811
				/** This filter is documented in core/src/wp-login.php */
812
				apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user )
813
			);
814
			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...
815
		}
816
817
		JetpackTracking::record_user_event( 'sso_login_failed', array(
818
			'error_message' => 'cant_find_user'
819
		) );
820
821
		$this->user_data = $user_data;
822
		/** This filter is documented in core/src/wp-includes/pluggable.php */
823
		do_action( 'wp_login_failed', $user_data->login );
824
		add_filter( 'login_message', array( $this, 'cant_find_user' ) );
825
	}
826
827
	static function profile_page_url() {
828
		return admin_url( 'profile.php' );
829
	}
830
831
	static function match_by_email() {
832
		$match_by_email = ( 1 == get_option( 'jetpack_sso_match_by_email', true ) ) ? true: false;
833
		$match_by_email = defined( 'WPCC_MATCH_BY_EMAIL' ) ? WPCC_MATCH_BY_EMAIL : $match_by_email;
834
835
		/**
836
		 * Link the local account to an account on WordPress.com using the same email address.
837
		 *
838
		 * @module sso
839
		 *
840
		 * @since 2.6.0
841
		 *
842
		 * @param bool $match_by_email Should we link the local account to an account on WordPress.com using the same email address. Default to false.
843
		 */
844
		return apply_filters( 'jetpack_sso_match_by_email', $match_by_email );
845
	}
846
847
	static function new_user_override() {
848
		$new_user_override = defined( 'WPCC_NEW_USER_OVERRIDE' ) ? WPCC_NEW_USER_OVERRIDE : false;
849
850
		/**
851
		 * Allow users to register on your site with a WordPress.com account, even though you disallow normal registrations.
852
		 *
853
		 * @module sso
854
		 *
855
		 * @since 2.6.0
856
		 *
857
		 * @param bool $new_user_override Allow users to register on your site with a WordPress.com account. Default to false.
858
		 */
859
		return apply_filters( 'jetpack_sso_new_user_override', $new_user_override );
860
	}
861
862
	function allowed_redirect_hosts( $hosts ) {
863
		if ( empty( $hosts ) ) {
864
			$hosts = array();
865
		}
866
		$hosts[] = 'wordpress.com';
867
		$hosts[] = 'jetpack.wordpress.com';
868
869
		return array_unique( $hosts );
870
	}
871
872
	/**
873
	 * Builds the "Login to WordPress.com" button that is displayed on the login page as well as user profile page.
874
	 *
875
	 * @param  array   $args       An array of arguments to add to the SSO URL.
876
	 * @param  boolean $is_primary Should the button have the `button-primary` class?
877
	 * @return string              Returns the HTML markup for the button.
878
	 */
879
	function build_sso_button( $args = array(), $is_primary = false ) {
880
		$url = $this->build_sso_button_url( $args );
881
		$classes = $is_primary
882
			? 'jetpack-sso button button-primary'
883
			: 'jetpack-sso button';
884
885
		return sprintf(
886
			'<a href="%1$s" class="%2$s">%3$s</a>',
887
			esc_url( $url ),
888
			$classes,
889
			esc_html__( 'Log in with WordPress.com', 'jetpack' )
890
		);
891
	}
892
893
	/**
894
	 * Builds a URL with `jetpack-sso` action and option args which is used to setup SSO.
895
	 *
896
	 * @param  array  $args An array of arguments to add to the SSO URL.
897
	 * @return string       The URL used for SSO.
898
	 */
899
	function build_sso_button_url( $args = array() ) {
900
		$defaults = array(
901
			'action'  => 'jetpack-sso',
902
		);
903
904
		$args = wp_parse_args( $args, $defaults );
905
906
		if ( ! empty( $_GET['redirect_to'] ) ) {
907
			$args['redirect_to'] = esc_url_raw( $_GET['redirect_to'] );
908
		}
909
910
		return add_query_arg( $args, wp_login_url() );
911
	}
912
913
	/**
914
	 * Retrieves a WordPress.com SSO URL with appropriate query parameters or dies.
915
	 *
916
	 * @param  boolean  $reauth  Should the user be forced to reauthenticate on WordPress.com?
917
	 * @param  array    $args    Optional query parameters.
918
	 * @return string            The WordPress.com SSO URL.
919
	 */
920
	function get_sso_url_or_die( $reauth = false, $args = array() ) {
921
		if ( empty( $reauth ) ) {
922
			$sso_redirect = $this->build_sso_url( $args );
923
		} else {
924
			self::clear_wpcom_profile_cookies();
925
			$sso_redirect = $this->build_reauth_and_sso_url( $args );
926
		}
927
928
		// If there was an error retrieving the SSO URL, then error.
929
		if ( is_wp_error( $sso_redirect ) ) {
930
			$error_message = sanitize_text_field(
931
				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...
932
			);
933
			JetpackTracking::record_user_event( 'sso_login_redirect_failed', array(
934
				'error_message' => $error_message
935
			) );
936
			wp_die( $error_message );
937
		}
938
939
		return $sso_redirect;
940
	}
941
942
	/**
943
	 * Build WordPress.com SSO URL with appropriate query parameters.
944
	 *
945
	 * @param  array  $args Optional query parameters.
946
	 * @return string       WordPress.com SSO URL
947
	 */
948
	function build_sso_url( $args = array() ) {
949
		$sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
950
		$defaults = array(
951
			'action'    => 'jetpack-sso',
952
			'site_id'   => Jetpack_Options::get_option( 'id' ),
953
			'sso_nonce' => $sso_nonce,
954
		);
955
956
		$args = wp_parse_args( $args, $defaults );
957
958
		if ( is_wp_error( $args['sso_nonce'] ) ) {
959
			return $args['sso_nonce'];
960
		}
961
962
		return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
963
	}
964
965
	/**
966
	 * Build WordPress.com SSO URL with appropriate query parameters,
967
	 * including the parameters necessary to force the user to reauthenticate
968
	 * on WordPress.com.
969
	 *
970
	 * @param  array  $args Optional query parameters.
971
	 * @return string       WordPress.com SSO URL
972
	 */
973
	function build_reauth_and_sso_url( $args = array() ) {
974
		$sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
975
		$redirect = $this->build_sso_url( array( 'force_auth' => '1', 'sso_nonce' => $sso_nonce ) );
976
977
		if ( is_wp_error( $redirect ) ) {
978
			return $redirect;
979
		}
980
981
		$defaults = array(
982
			'action'      => 'jetpack-sso',
983
			'site_id'     => Jetpack_Options::get_option( 'id' ),
984
			'sso_nonce'   => $sso_nonce,
985
			'reauth'      => '1',
986
			'redirect_to' => urlencode( $redirect ),
987
		);
988
989
		$args = wp_parse_args( $args, $defaults );
990
991
		if ( is_wp_error( $args['sso_nonce'] ) ) {
992
			return $args['sso_nonce'];
993
		}
994
995
		return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
996
	}
997
998
	/**
999
	 * Determines local user associated with a given WordPress.com user ID.
1000
	 *
1001
	 * @since 2.6.0
1002
	 *
1003
	 * @param int $wpcom_user_id User ID from WordPress.com
1004
	 * @return object Local user object if found, null if not.
1005
	 */
1006
	static function get_user_by_wpcom_id( $wpcom_user_id ) {
1007
		$user_query = new WP_User_Query( array(
1008
			'meta_key'   => 'wpcom_user_id',
1009
			'meta_value' => intval( $wpcom_user_id ),
1010
			'number'     => 1,
1011
		) );
1012
1013
		$users = $user_query->get_results();
1014
		return $users ? array_shift( $users ) : null;
1015
	}
1016
1017
	/**
1018
	 * Error message displayed on the login form when two step is required and
1019
	 * the user's account on WordPress.com does not have two step enabled.
1020
	 *
1021
	 * @since 2.7
1022
	 * @param string $message
1023
	 * @return string
1024
	 **/
1025
	public function error_msg_enable_two_step( $message ) {
1026
		$error = sprintf(
1027
			wp_kses(
1028
				__(
1029
					'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.',
1030
					'jetpack'
1031
				),
1032
				array(  'a' => array( 'href' => array() ) )
1033
			),
1034
			'https://wordpress.com/me/security/two-step',
1035
			'https://support.wordpress.com/security/two-step-authentication/'
1036
		);
1037
1038
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
1039
1040
		return $message;
1041
	}
1042
1043
	/**
1044
	 * Error message displayed when the user tries to SSO, but match by email
1045
	 * is off and they already have an account with their email address on
1046
	 * this site.
1047
	 *
1048
	 * @param string $message
1049
	 * @return string
1050
	 */
1051
	public function error_msg_email_already_exists( $message ) {
1052
		$error = sprintf(
1053
			wp_kses(
1054
				__(
1055
					'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.',
1056
					'jetpack'
1057
				),
1058
				array(  'a' => array( 'href' => array() ) )
1059
			),
1060
			esc_url_raw( add_query_arg( 'jetpack-sso-default-form', '1', wp_login_url() ) )
1061
		);
1062
1063
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
1064
1065
		return $message;
1066
	}
1067
1068
	/**
1069
	 * Message displayed when the site admin has disabled the default WordPress
1070
	 * login form in Settings > General > Single Sign On
1071
	 *
1072
	 * @since 2.7
1073
	 * @param string $message
1074
	 * @return string
1075
	 **/
1076
	public function msg_login_by_jetpack( $message ) {
1077
		$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' );
1078
1079
		/**
1080
		 * Filter the message displayed when the default WordPress login form is disabled.
1081
		 *
1082
		 * @module sso
1083
		 *
1084
		 * @since 2.8.0
1085
		 *
1086
		 * @param string $msg Disclaimer when default WordPress login form is disabled.
1087
		 */
1088
		$msg = apply_filters( 'jetpack_sso_disclaimer_message', $msg );
1089
1090
		if ( empty( $msg ) ) {
1091
			return $message;
1092
		}
1093
1094
		$message .= sprintf( '<p class="message">%s</p>', $msg );
1095
		return $message;
1096
	}
1097
1098
	/**
1099
	 * Message displayed when the user can not be found after approving the SSO process on WordPress.com
1100
	 *
1101
	 * @param string $message
1102
	 * @return string
1103
	 */
1104
	function cant_find_user( $message ) {
1105
		$error = esc_html__(
1106
			"We couldn't find your account. If you already have an account, make sure you have connected to WordPress.com.",
1107
			'jetpack'
1108
		);
1109
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
1110
1111
		return $message;
1112
	}
1113
1114
	/**
1115
	 * When jetpack-sso-auth-redirect query parameter is set, will redirect user to
1116
	 * WordPress.com authorization flow.
1117
	 *
1118
	 * We redirect here instead of in handle_login() because Jetpack::init()->build_connect_url
1119
	 * calls menu_page_url() which doesn't work properly until admin menus are registered.
1120
	 */
1121
	function maybe_authorize_user_after_sso() {
1122
		if ( empty( $_GET['jetpack-sso-auth-redirect'] ) ) {
1123
			return;
1124
		}
1125
1126
		$redirect_to = ! empty( $_GET['redirect_to'] ) ? esc_url_raw( $_GET['redirect_to'] ) : admin_url();
1127
		$request_redirect_to = ! empty( $_GET['request_redirect_to'] ) ? esc_url_raw( $_GET['request_redirect_to'] ) : $redirect_to;
1128
1129
		/** This filter is documented in core/src/wp-login.php */
1130
		$redirect_after_auth = apply_filters( 'login_redirect', $redirect_to, $request_redirect_to, wp_get_current_user() );
1131
1132
		/**
1133
		 * Since we are passing this redirect to WordPress.com and therefore can not use wp_safe_redirect(),
1134
		 * let's sanitize it here to make sure it's safe. If the redirect is not safe, then use admin_url().
1135
		 */
1136
		$redirect_after_auth = wp_sanitize_redirect( $redirect_after_auth );
1137
		$redirect_after_auth = wp_validate_redirect( $redirect_after_auth, admin_url() );
1138
1139
		/**
1140
		 * Return the raw connect URL with our redirect and attribute connection to SSO.
1141
		 */
1142
		$connect_url = Jetpack::init()->build_connect_url( true, $redirect_after_auth, 'sso' );
1143
1144
		add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
1145
		wp_safe_redirect( $connect_url );
1146
		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...
1147
	}
1148
1149
	/**
1150
	 * Cache user's display name and Gravatar so it can be displayed on the login screen. These cookies are
1151
	 * stored when the user logs out, and then deleted when the user logs in.
1152
	 */
1153
	function store_wpcom_profile_cookies_on_logout() {
1154
		if ( ! Jetpack::is_user_connected( get_current_user_id() ) ) {
1155
			return;
1156
		}
1157
1158
		$user_data = $this->get_user_data( get_current_user_id() );
1159
		if ( ! $user_data ) {
1160
			return;
1161
		}
1162
1163
		setcookie(
1164
			'jetpack_sso_wpcom_name_' . COOKIEHASH,
1165
			$user_data->display_name,
1166
			time() + WEEK_IN_SECONDS,
1167
			COOKIEPATH,
1168
			COOKIE_DOMAIN
1169
		);
1170
1171
		setcookie(
1172
			'jetpack_sso_wpcom_gravatar_' . COOKIEHASH,
1173
			get_avatar_url(
1174
				$user_data->email,
1175
				array( 'size' => 72, 'default' => 'mystery' )
1176
			),
1177
			time() + WEEK_IN_SECONDS,
1178
			COOKIEPATH,
1179
			COOKIE_DOMAIN
1180
		);
1181
	}
1182
1183
	/**
1184
	 * Determines if a local user is connected to WordPress.com
1185
	 *
1186
	 * @since 2.8
1187
	 * @param integer $user_id - Local user id
1188
	 * @return boolean
1189
	 **/
1190
	public function is_user_connected( $user_id ) {
1191
		return $this->get_user_data( $user_id );
1192
	}
1193
1194
	/**
1195
	 * Retrieves a user's WordPress.com data
1196
	 *
1197
	 * @since 2.8
1198
	 * @param integer $user_id - Local user id
1199
	 * @return mixed null or stdClass
1200
	 **/
1201
	public function get_user_data( $user_id ) {
1202
		return get_user_meta( $user_id, 'wpcom_user_data', true );
1203
	}
1204
}
1205
1206
Jetpack_SSO::get_instance();
1207