Completed
Push — 4.1.0/videopress-media-merge ( 234ba8...2b770e )
by George
106:45 queued 96:11
created

Jetpack_SSO::new_user_override()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 14
rs 9.4285
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 with WordPress.com.
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: Security, Jumpstart
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
		if ( Jetpack_SSO_Helpers::should_hide_login_form() ) {
41
			/**
42
			 * Since the default authenticate filters fire at priority 20 for checking username and password,
43
			 * let's fire at priority 30. wp_authenticate_spam_check is fired at priority 99, but since we return a
44
			 * WP_Error in disable_default_login_form, then we won't trigger spam processing logic.
45
			 */
46
			add_filter( 'authenticate', array( $this, 'disable_default_login_form' ), 30 );
47
48
			/**
49
			 * Filter the display of the disclaimer message appearing when default WordPress login form is disabled.
50
			 *
51
			 * @module sso
52
			 *
53
			 * @since 2.8.0
54
			 *
55
			 * @param bool true Should the disclaimer be displayed. Default to true.
56
			 */
57
			$display_sso_disclaimer = apply_filters( 'jetpack_sso_display_disclaimer', true );
58
			if ( $display_sso_disclaimer ) {
59
				add_filter( 'login_message', array( $this, 'msg_login_by_jetpack' ) );
60
			}
61
		}
62
	}
63
64
	/**
65
	 * Returns the single instance of the Jetpack_SSO object
66
	 *
67
	 * @since 2.8
68
	 * @return Jetpack_SSO
69
	 **/
70
	public static function get_instance() {
71
		if ( ! is_null( self::$instance ) ) {
72
			return self::$instance;
73
		}
74
75
		return self::$instance = new Jetpack_SSO;
76
	}
77
78
	/**
79
	 * Add configure button and functionality to the module card on the Jetpack screen
80
	 **/
81
	public static function module_configure_button() {
82
		Jetpack::enable_module_configurable( __FILE__ );
83
		Jetpack::module_configuration_load( __FILE__, array( __CLASS__, 'module_configuration_load' ) );
84
		Jetpack::module_configuration_head( __FILE__, array( __CLASS__, 'module_configuration_head' ) );
85
		Jetpack::module_configuration_screen( __FILE__, array( __CLASS__, 'module_configuration_screen' ) );
86
	}
87
88
	public static function module_configuration_load() {}
89
90
	public static function module_configuration_head() {}
91
92
	public static function module_configuration_screen() {
93
		?>
94
		<form method="post" action="options.php">
95
			<?php settings_fields( 'jetpack-sso' ); ?>
96
			<?php do_settings_sections( 'jetpack-sso' ); ?>
97
			<?php submit_button(); ?>
98
		</form>
99
		<?php
100
	}
101
102
103
	/**
104
	 * When the default login form is hidden, this method is called on the 'authenticate' filter with a priority of 30.
105
	 * This method disables the ability to submit the default login form.
106
	 *
107
	 * @param $user
108
	 *
109
	 * @return WP_Error
110
	 */
111
	public function disable_default_login_form( $user ) {
112
		if ( is_wp_error( $user ) ) {
113
			return $user;
114
		}
115
116
		/**
117
		 * Since we're returning an error that will be shown as a red notice, let's remove the
118
		 * informational "blue" notice.
119
		 */
120
		remove_filter( 'login_message', array( $this, 'msg_login_by_jetpack' ) );
121
		return new WP_Error( 'jetpack_sso_required', $this->get_sso_required_message() );
122
	}
123
124
	/**
125
	 * If jetpack_force_logout == 1 in current user meta the user will be forced
126
	 * to logout and reauthenticate with the site.
127
	 **/
128
	public function maybe_logout_user() {
129
		global $current_user;
130
131
		if ( 1 == $current_user->jetpack_force_logout ) {
132
			delete_user_meta( $current_user->ID, 'jetpack_force_logout' );
133
			self::delete_connection_for_user( $current_user->ID );
134
			wp_logout();
135
			wp_safe_redirect( wp_login_url() );
136
			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...
137
		}
138
	}
139
140
141
	/**
142
	 * Adds additional methods the WordPress xmlrpc API for handling SSO specific features
143
	 *
144
	 * @param array $methods
145
	 * @return array
146
	 **/
147
	public function xmlrpc_methods( $methods ) {
148
		$methods['jetpack.userDisconnect'] = array( $this, 'xmlrpc_user_disconnect' );
149
		return $methods;
150
	}
151
152
	/**
153
	 * Marks a user's profile for disconnect from WordPress.com and forces a logout
154
	 * the next time the user visits the site.
155
	 **/
156
	public function xmlrpc_user_disconnect( $user_id ) {
157
		$user_query = new WP_User_Query(
158
			array(
159
				'meta_key' => 'wpcom_user_id',
160
				'meta_value' => $user_id,
161
			)
162
		);
163
		$user = $user_query->get_results();
164
		$user = $user[0];
165
166
		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...
167
			$user = wp_set_current_user( $user->ID );
168
			update_user_meta( $user->ID, 'jetpack_force_logout', '1' );
169
			self::delete_connection_for_user( $user->ID );
170
			return true;
171
		}
172
		return false;
173
	}
174
175
	/**
176
	 * Enqueues scripts and styles necessary for SSO login.
177
	 */
178
	public function login_enqueue_scripts() {
179
		global $action;
180
181
		if ( ! in_array( $action, array( 'jetpack-sso', 'login' ) ) ) {
182
			return;
183
		}
184
185
		if ( is_rtl() ) {
186
			wp_enqueue_style( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login-rtl.css', JETPACK__PLUGIN_FILE ), array( 'login', 'genericons' ), JETPACK__VERSION );
187
		} else {
188
			wp_enqueue_style( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.css', JETPACK__PLUGIN_FILE ), array( 'login', 'genericons' ), JETPACK__VERSION );
189
		}
190
191
		wp_enqueue_script( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.js', JETPACK__PLUGIN_FILE ), array( 'jquery' ), JETPACK__VERSION );
192
	}
193
194
	/**
195
	 * Enqueue styles neceessary for Jetpack SSO on users' profiles
196
	 */
197
	public function admin_enqueue_scripts() {
198
		$screen = get_current_screen();
199
200
		if ( empty( $screen ) || ! in_array( $screen->base, array( 'edit-user', 'profile' ) ) ) {
201
			return;
202
		}
203
204
		wp_enqueue_style( 'jetpack-sso-profile', plugins_url( 'modules/sso/jetpack-sso-profile.css', JETPACK__PLUGIN_FILE ), array( 'genericons' ), JETPACK__VERSION );
205
	}
206
207
	/**
208
	 * Adds Jetpack SSO classes to login body
209
	 *
210
	 * @param  array $classes Array of classes to add to body tag
211
	 * @return array          Array of classes to add to body tag
212
	 */
213
	public function login_body_class( $classes ) {
214
		global $action;
215
216
		if ( ! in_array( $action, array( 'jetpack-sso', 'login' ) ) ) {
217
			return $classes;
218
		}
219
220
		//Always add the jetpack-sso class so that we can add SSO specific styling even when the SSO form isn't being displayed.
221
		$classes[] = 'jetpack-sso';
222
223
		// If jetpack-sso-default-form, show the default login form.
224
		if ( isset( $_GET['jetpack-sso-default-form'] ) && 1 == $_GET['jetpack-sso-default-form'] ) {
225
			return $classes;
226
		}
227
228
		$classes[] = 'jetpack-sso-form-display';
229
		return $classes;
230
	}
231
232
	/**
233
	 * Adds settings fields to Settings > General > Single Sign On that allows users to
234
	 * turn off the login form on wp-login.php
235
	 *
236
	 * @since 2.7
237
	 **/
238
	public function register_settings() {
239
240
		add_settings_section(
241
			'jetpack_sso_settings',
242
			__( 'Single Sign On' , 'jetpack' ),
243
			'__return_false',
244
			'jetpack-sso'
245
		);
246
247
		/*
248
		 * Settings > General > Single Sign On
249
		 * Require two step authentication
250
		 */
251
		register_setting(
252
			'jetpack-sso',
253
			'jetpack_sso_require_two_step',
254
			array( $this, 'validate_jetpack_sso_require_two_step' )
255
		);
256
257
		add_settings_field(
258
			'jetpack_sso_require_two_step',
259
			'', // __( 'Require Two-Step Authentication' , 'jetpack' ),
260
			array( $this, 'render_require_two_step' ),
261
			'jetpack-sso',
262
			'jetpack_sso_settings'
263
		);
264
265
		/*
266
		 * Settings > General > Single Sign On
267
		 */
268
		register_setting(
269
			'jetpack-sso',
270
			'jetpack_sso_match_by_email',
271
			array( $this, 'validate_jetpack_sso_match_by_email' )
272
		);
273
274
		add_settings_field(
275
			'jetpack_sso_match_by_email',
276
			'', // __( 'Match by Email' , 'jetpack' ),
277
			array( $this, 'render_match_by_email' ),
278
			'jetpack-sso',
279
			'jetpack_sso_settings'
280
		);
281
	}
282
283
	/**
284
	 * Builds the display for the checkbox allowing user to require two step
285
	 * auth be enabled on WordPress.com accounts before login. Displays in Settings > General
286
	 *
287
	 * @since 2.7
288
	 **/
289 View Code Duplication
	public function render_require_two_step() {
290
		/** This filter is documented in modules/sso.php */
291
		$require_two_step = Jetpack_SSO_Helpers::is_two_step_required();
292
		$disabled = $require_two_step ? ' disabled="disabled"' : '';
293
		echo '<label>';
294
		echo '<input type="checkbox" name="jetpack_sso_require_two_step" ' . checked( $require_two_step, true, false ) . "$disabled>";
295
		esc_html_e( 'Require Two-Step Authentication' , 'jetpack' );
296
		echo '</label>';
297
	}
298
299
	/**
300
	 * Validate the require  two step checkbox in Settings > General
301
	 *
302
	 * @since 2.7
303
	 * @return boolean
304
	 **/
305
	public function validate_jetpack_sso_require_two_step( $input ) {
306
		return ( ! empty( $input ) ) ? 1 : 0;
307
	}
308
309
	/**
310
	 * Builds the display for the checkbox allowing the user to allow matching logins by email
311
	 * Displays in Settings > General
312
	 *
313
	 * @since 2.9
314
	 **/
315 View Code Duplication
	public function render_match_by_email() {
316
		$match_by_email = 1 == $this->match_by_email();
0 ignored issues
show
Bug introduced by
The method match_by_email() does not exist on Jetpack_SSO. Did you maybe mean render_match_by_email()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
317
		$disabled = $match_by_email ? ' disabled="disabled"' : '';
318
		echo '<label>';
319
		echo '<input type="checkbox" name="jetpack_sso_match_by_email"' . checked( $match_by_email, true, false ) . "$disabled>";
320
		esc_html_e( 'Match by Email', 'jetpack' );
321
		echo '</label>';
322
	}
323
324
	/**
325
	 * Validate the match by email check in Settings > General
326
	 *
327
	 * @since 2.9
328
	 * @return boolean
329
	 **/
330
	public function validate_jetpack_sso_match_by_email( $input ) {
331
		return ( ! empty( $input ) ) ? 1 : 0;
332
	}
333
334
	/**
335
	 * Checks to determine if the user wants to login on wp-login
336
	 *
337
	 * This function mostly exists to cover the exceptions to login
338
	 * that may exist as other parameters to $_GET[action] as $_GET[action]
339
	 * does not have to exist. By default WordPress assumes login if an action
340
	 * is not set, however this may not be true, as in the case of logout
341
	 * where $_GET[loggedout] is instead set
342
	 *
343
	 * @return boolean
344
	 **/
345
	private function wants_to_login() {
346
		$wants_to_login = false;
347
348
		// Cover default WordPress behavior
349
		$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'login';
350
351
		// And now the exceptions
352
		$action = isset( $_GET['loggedout'] ) ? 'loggedout' : $action;
353
354
		if ( 'login' == $action ) {
355
			$wants_to_login = true;
356
		}
357
358
		return $wants_to_login;
359
	}
360
361
	function login_init() {
362
		global $action;
363
364
		/**
365
		 * If the user is attempting to logout AND the auto-forward to WordPress.com
366
		 * login is set then we need to ensure we do not auto-forward the user and get
367
		 * them stuck in an infinite logout loop.
368
		 */
369
		if ( isset( $_GET['loggedout'] ) && Jetpack_SSO_Helpers::bypass_login_forward_wpcom() ) {
370
			add_filter( 'jetpack_remove_login_form', '__return_true' );
371
		}
372
373
		/**
374
		 * Check to see if the site admin wants to automagically forward the user
375
		 * to the WordPress.com login page AND  that the request to wp-login.php
376
		 * is not something other than login (Like logout!)
377
		 */
378 View Code Duplication
		if (
379
			$this->wants_to_login()
380
			&& Jetpack_SSO_Helpers::bypass_login_forward_wpcom()
381
		) {
382
			add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
383
			$this->maybe_save_cookie_redirect();
384
			$reauth = ! empty( $_GET['force_reauth'] );
385
			$sso_url = $this->get_sso_url_or_die( $reauth );
386
			JetpackTracking::record_user_event( 'sso_login_redirect_bypass_success' );
387
			wp_safe_redirect( $sso_url );
388
			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...
389
		}
390
391
		if ( 'login' === $action ) {
392
			$this->display_sso_login_form();
393
		} elseif ( 'jetpack-sso' === $action ) {
394
			if ( isset( $_GET['result'], $_GET['user_id'], $_GET['sso_nonce'] ) && 'success' == $_GET['result'] ) {
395
				$this->handle_login();
396
				$this->display_sso_login_form();
397
			} else {
398
				if ( Jetpack::check_identity_crisis() ) {
399
					JetpackTracking::record_user_event( 'sso_login_redirect_failed', array(
400
						'error_message' => 'identity_crisis'
401
					) );
402
					wp_die( __( "Error: This site's Jetpack connection is currently experiencing problems.", 'jetpack' ) );
403 View Code Duplication
				} else {
404
					$this->maybe_save_cookie_redirect();
405
					// Is it wiser to just use wp_redirect than do this runaround to wp_safe_redirect?
406
					add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
407
					$reauth = ! empty( $_GET['force_reauth'] );
408
					$sso_url = $this->get_sso_url_or_die( $reauth );
409
					JetpackTracking::record_user_event( 'sso_login_redirect_success' );
410
					wp_safe_redirect( $sso_url );
411
					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...
412
				}
413
			}
414
		}
415
	}
416
417
	/**
418
	 * Ensures that we can get a nonce from WordPress.com via XML-RPC before setting
419
	 * up the hooks required to display the SSO form.
420
	 */
421
	public function display_sso_login_form() {
422
		$sso_nonce = self::request_initial_nonce();
423
		if ( is_wp_error( $sso_nonce ) ) {
424
			return;
425
		}
426
427
		add_action( 'login_form',            array( $this, 'login_form' ) );
428
		add_filter( 'login_body_class',      array( $this, 'login_body_class' ) );
429
		add_action( 'login_enqueue_scripts', array( $this, 'login_enqueue_scripts' ) );
430
	}
431
432
	/**
433
	 * Conditionally save the redirect_to url as a cookie.
434
	 */
435
	public static function maybe_save_cookie_redirect() {
436
		if ( headers_sent() ) {
437
			return new WP_Error( 'headers_sent', __( 'Cannot deal with cookie redirects, as headers are already sent.', 'jetpack' ) );
438
		}
439
440
		if ( ! empty( $_GET['redirect_to'] ) ) {
441
			// If we have something to redirect to
442
			$url = esc_url_raw( $_GET['redirect_to'] );
443
			setcookie( 'jetpack_sso_redirect_to', $url, time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
444
445
		} elseif ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
446
			// Otherwise, if it's already set, purge it.
447
			setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
448
		}
449
450
		if ( ! empty( $_GET['rememberme'] ) ) {
451
			setcookie( 'jetpack_sso_remember_me', '1', time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
452
		} elseif ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
453
			setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
454
		}
455
	}
456
457
	/**
458
	 * Outputs the Jetpack SSO button and description as well as the toggle link
459
	 * for switching between Jetpack SSO and default login.
460
	 */
461
	function login_form() {
462
		$site_name = get_bloginfo( 'name' );
463
		if ( ! $site_name ) {
464
			$site_name = get_bloginfo( 'url' );
465
		}
466
467
		$display_name = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] )
468
			? $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ]
469
			: false;
470
		$gravatar = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] )
471
			? $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ]
472
			: false;
473
474
		?>
475
		<div id="jetpack-sso-wrap">
476
			<?php if ( $display_name && $gravatar ) : ?>
477
				<div id="jetpack-sso-wrap__user">
478
					<img width="72" height="72" src="<?php echo esc_html( $gravatar ); ?>" />
479
480
					<h2>
481
						<?php
482
							echo wp_kses(
483
								sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
484
								array( 'span' => true )
485
							);
486
						?>
487
					</h2>
488
				</div>
489
490
			<?php endif; ?>
491
492
493
			<div id="jetpack-sso-wrap__action">
494
				<?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...
495
496
				<?php if ( $display_name && $gravatar ) : ?>
497
					<a rel="nofollow" class="jetpack-sso-wrap__reauth" href="<?php echo esc_url( $this->build_sso_button_url( array( 'force_reauth' => '1' ) ) ); ?>">
498
						<?php esc_html_e( 'Log in as a different WordPress.com user', 'jetpack' ); ?>
499
					</a>
500
				<?php else : ?>
501
					<p>
502
						<?php
503
							echo esc_html(
504
								sprintf(
505
									__( 'You can now save time spent logging in by connecting your WordPress.com account to %s.', 'jetpack' ),
506
									esc_html( $site_name )
507
								)
508
							);
509
						?>
510
					</p>
511
				<?php endif; ?>
512
			</div>
513
514
			<?php if ( ! Jetpack_SSO_Helpers::should_hide_login_form() ) : ?>
515
				<div class="jetpack-sso-or">
516
					<span><?php esc_html_e( 'Or', 'jetpack' ); ?></span>
517
				</div>
518
519
				<a href="<?php echo add_query_arg( 'jetpack-sso-default-form', '1' ); ?>" class="jetpack-sso-toggle wpcom">
520
					<?php
521
						esc_html_e( 'Log in with username and password', 'jetpack' )
522
					?>
523
				</a>
524
525
				<a href="<?php echo add_query_arg( 'jetpack-sso-default-form', '0' ); ?>" class="jetpack-sso-toggle default">
526
					<?php
527
						esc_html_e( 'Log in with WordPress.com', 'jetpack' )
528
					?>
529
				</a>
530
			<?php endif; ?>
531
		</div>
532
		<?php
533
	}
534
535
	/**
536
	 * Clear the cookies that store the profile information for the last
537
	 * WPCOM user to connect.
538
	 */
539
	static function clear_wpcom_profile_cookies() {
540 View Code Duplication
		if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) ) {
541
			setcookie(
542
				'jetpack_sso_wpcom_name_' . COOKIEHASH,
543
				' ',
544
				time() - YEAR_IN_SECONDS,
545
				COOKIEPATH,
546
				COOKIE_DOMAIN
547
			);
548
		}
549
550 View Code Duplication
		if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) ) {
551
			setcookie(
552
				'jetpack_sso_wpcom_gravatar_' . COOKIEHASH,
553
				' ',
554
				time() - YEAR_IN_SECONDS,
555
				COOKIEPATH,
556
				COOKIE_DOMAIN
557
			);
558
		}
559
	}
560
561
	static function delete_connection_for_user( $user_id ) {
562
		if ( ! $wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true ) ) {
563
			return;
564
		}
565
		Jetpack::load_xml_rpc_client();
566
		$xml = new Jetpack_IXR_Client( array(
567
			'wpcom_user_id' => $user_id,
568
		) );
569
		$xml->query( 'jetpack.sso.removeUser', $wpcom_user_id );
570
571
		if ( $xml->isError() ) {
572
			return false;
573
		}
574
575
		// Clean up local data stored for SSO
576
		delete_user_meta( $user_id, 'wpcom_user_id' );
577
		delete_user_meta( $user_id, 'wpcom_user_data'  );
578
		self::clear_wpcom_profile_cookies();
579
580
		return $xml->getResponse();
581
	}
582
583 View Code Duplication
	static function request_initial_nonce() {
584
		Jetpack::load_xml_rpc_client();
585
		$xml = new Jetpack_IXR_Client( array(
586
			'user_id' => get_current_user_id(),
587
		) );
588
		$xml->query( 'jetpack.sso.requestNonce' );
589
590
		if ( $xml->isError() ) {
591
			return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() );
592
		}
593
594
		return $xml->getResponse();
595
	}
596
597
	/**
598
	 * The function that actually handles the login!
599
	 */
600
	function handle_login() {
601
		$wpcom_nonce   = sanitize_key( $_GET['sso_nonce'] );
602
		$wpcom_user_id = (int) $_GET['user_id'];
603
604
		Jetpack::load_xml_rpc_client();
605
		$xml = new Jetpack_IXR_Client( array(
606
			'user_id' => get_current_user_id(),
607
		) );
608
		$xml->query( 'jetpack.sso.validateResult', $wpcom_nonce, $wpcom_user_id );
609
610
		if ( $xml->isError() ) {
611
			$error_message = sanitize_text_field(
612
				sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() )
613
			);
614
			JetpackTracking::record_user_event( 'sso_login_failed', array(
615
				'error_message' => $error_message
616
			) );
617
			wp_die( $error_message );
618
		}
619
620
		$user_data = $xml->getResponse();
621
622
		if ( empty( $user_data ) ) {
623
			JetpackTracking::record_user_event( 'sso_login_failed', array(
624
				'error_message' => 'invalid_response_data'
625
			) );
626
			wp_die( __( 'Error, invalid response data.', 'jetpack' ) );
627
		}
628
629
		$user_data = (object) $user_data;
630
		$user = null;
631
632
		/**
633
		 * Fires before Jetpack's SSO modifies the log in form.
634
		 *
635
		 * @module sso
636
		 *
637
		 * @since 2.6.0
638
		 *
639
		 * @param object $user_data User login information.
640
		 */
641
		do_action( 'jetpack_sso_pre_handle_login', $user_data );
642
643
		if ( Jetpack_SSO_Helpers::is_two_step_required() && 0 === (int) $user_data->two_step_enabled ) {
644
			$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...
645
646
			JetpackTracking::record_user_event( 'sso_login_failed', array(
647
				'error_message' => 'error_msg_enable_two_step'
648
			) );
649
			
650
			/** This filter is documented in core/src/wp-includes/pluggable.php */
651
			do_action( 'wp_login_failed', $user_data->login );
652
			add_filter( 'login_message', array( $this, 'error_msg_enable_two_step' ) );
653
			return;
654
		}
655
656
		$user_found_with = '';
657
		if ( empty( $user ) && isset( $user_data->external_user_id ) ) {
658
			$user_found_with = 'external_user_id';
659
			$user = get_user_by( 'id', intval( $user_data->external_user_id ) );
660
			if ( $user ) {
661
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
662
			}
663
		}
664
665
		// If we don't have one by wpcom_user_id, try by the email?
666
		if ( empty( $user ) && Jetpack_SSO_Helpers::match_by_email() ) {
667
			$user_found_with = 'match_by_email';
668
			$user = get_user_by( 'email', $user_data->email );
669
			if ( $user ) {
670
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
671
			}
672
		}
673
674
		// If we've still got nothing, create the user.
675
		if ( empty( $user ) && ( get_option( 'users_can_register' ) || Jetpack_SSO_Helpers::new_user_override() ) ) {
676
			// If not matching by email we still need to verify the email does not exist
677
			// or this blows up
678
			/**
679
			 * If match_by_email is true, we know the email doesn't exist, as it would have
680
			 * been found in the first pass.  If get_user_by( 'email' ) doesn't find the
681
			 * user, then we know that email is unused, so it's safe to add.
682
			 */
683
			if ( Jetpack_SSO_Helpers::match_by_email() || ! get_user_by( 'email', $user_data->email ) ) {
684
				$username = $user_data->login;
685
686
				if ( username_exists( $username ) ) {
687
					$username = $user_data->login . '_' . $user_data->ID;
688
				}
689
690
				$tries = 0;
691
				while ( username_exists( $username ) ) {
692
					$username = $user_data->login . '_' . $user_data->ID . '_' . mt_rand();
693
					if ( $tries++ >= 5 ) {
694
						JetpackTracking::record_user_event( 'sso_login_failed', array(
695
							'error_message' => 'could_not_create_username'
696
						) );
697
						wp_die( __( "Error: Couldn't create suitable username.", 'jetpack' ) );
698
					}
699
				}
700
701
				$user_found_with = Jetpack_SSO_Helpers::new_user_override()
702
					? 'user_created_new_user_override'
703
					: 'user_created_users_can_register';
704
705
				$password = wp_generate_password( 20 );
706
				$user_id  = wp_create_user( $username, $password, $user_data->email );
707
				$user     = get_userdata( $user_id );
708
709
				$user->display_name = $user_data->display_name;
710
				$user->first_name   = $user_data->first_name;
711
				$user->last_name    = $user_data->last_name;
712
				$user->url          = $user_data->url;
713
				$user->description  = $user_data->description;
714
				wp_update_user( $user );
715
716
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
717
			} else {
718
				JetpackTracking::record_user_event( 'sso_login_failed', array(
719
					'error_message' => 'error_msg_email_already_exists'
720
				) );
721
722
				$this->user_data = $user_data;
723
				add_action( 'login_message', array( $this, 'error_msg_email_already_exists' ) );
724
				return;
725
			}
726
		}
727
728
		/**
729
		 * Fires after we got login information from WordPress.com.
730
		 *
731
		 * @module sso
732
		 *
733
		 * @since 2.6.0
734
		 *
735
		 * @param array $user WordPress.com User information.
736
		 * @param object $user_data User Login information.
737
		 */
738
		do_action( 'jetpack_sso_handle_login', $user, $user_data );
739
740
		if ( $user ) {
741
			// Cache the user's details, so we can present it back to them on their user screen
742
			update_user_meta( $user->ID, 'wpcom_user_data', $user_data );
743
744
			$remember = false;
745 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
746
				$remember = true;
747
				// And then purge it
748
				setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
749
			}
750
			/**
751
			 * Filter the remember me value.
752
			 *
753
			 * @module sso
754
			 *
755
			 * @since 2.8.0
756
			 *
757
			 * @param bool $remember Is the remember me option checked?
758
			 */
759
			$remember = apply_filters( 'jetpack_remember_login', $remember );
760
			wp_set_auth_cookie( $user->ID, $remember );
761
762
			/** This filter is documented in core/src/wp-includes/user.php */
763
			do_action( 'wp_login', $user->user_login, $user );
764
765
			wp_set_current_user( $user->ID );
766
767
			$_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( $_REQUEST['redirect_to'] ) : '';
768
			$redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : self::profile_page_url();
769
770
			// If we have a saved redirect to request in a cookie
771 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
772
				// Set that as the requested redirect to
773
				$redirect_to = $_request_redirect_to = esc_url_raw( $_COOKIE['jetpack_sso_redirect_to'] );
774
				// And then purge it
775
				setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
776
			}
777
778
			$is_user_connected = Jetpack::is_user_connected( $user->ID );
779
			JetpackTracking::record_user_event( 'sso_user_logged_in', array(
780
				'user_found_with' => $user_found_with,
781
				'user_connected'  => (bool) $is_user_connected,
782
				'user_role'       => Jetpack::init()->translate_current_user_to_role()
783
			) );
784
785
			if ( ! $is_user_connected ) {
786
				$calypso_env = ! empty( $_GET['calypso_env'] )
787
					? sanitize_key( $_GET['calypso_env'] )
788
					: '';
789
790
				wp_safe_redirect(
791
					add_query_arg(
792
						array(
793
							'redirect_to'               => $redirect_to,
794
							'request_redirect_to'       => $_request_redirect_to,
795
							'calypso_env'               => $calypso_env,
796
							'jetpack-sso-auth-redirect' => '1',
797
						),
798
						admin_url()
799
					)
800
				);
801
				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...
802
			}
803
804
			wp_safe_redirect(
805
				/** This filter is documented in core/src/wp-login.php */
806
				apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user )
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
		JetpackTracking::record_user_event( 'sso_login_failed', array(
812
			'error_message' => 'cant_find_user'
813
		) );
814
815
		$this->user_data = $user_data;
816
		/** This filter is documented in core/src/wp-includes/pluggable.php */
817
		do_action( 'wp_login_failed', $user_data->login );
818
		add_filter( 'login_message', array( $this, 'cant_find_user' ) );
819
	}
820
821
	static function profile_page_url() {
822
		return admin_url( 'profile.php' );
823
	}
824
825
	function allowed_redirect_hosts( $hosts ) {
826
		if ( empty( $hosts ) ) {
827
			$hosts = array();
828
		}
829
		$hosts[] = 'wordpress.com';
830
		$hosts[] = 'jetpack.wordpress.com';
831
832
		return array_unique( $hosts );
833
	}
834
835
	/**
836
	 * Builds the "Login to WordPress.com" button that is displayed on the login page as well as user profile page.
837
	 *
838
	 * @param  array   $args       An array of arguments to add to the SSO URL.
839
	 * @param  boolean $is_primary Should the button have the `button-primary` class?
840
	 * @return string              Returns the HTML markup for the button.
841
	 */
842
	function build_sso_button( $args = array(), $is_primary = false ) {
843
		$url = $this->build_sso_button_url( $args );
844
		$classes = $is_primary
845
			? 'jetpack-sso button button-primary'
846
			: 'jetpack-sso button';
847
848
		return sprintf(
849
			'<a rel="nofollow" href="%1$s" class="%2$s"><span>%3$s %4$s</span></a>',
850
			esc_url( $url ),
851
			$classes,
852
			'<span class="genericon genericon-wordpress"></span>',
853
			esc_html__( 'Log in with WordPress.com', 'jetpack' )
854
		);
855
	}
856
857
	/**
858
	 * Builds a URL with `jetpack-sso` action and option args which is used to setup SSO.
859
	 *
860
	 * @param  array  $args An array of arguments to add to the SSO URL.
861
	 * @return string       The URL used for SSO.
862
	 */
863
	function build_sso_button_url( $args = array() ) {
864
		$defaults = array(
865
			'action'  => 'jetpack-sso',
866
		);
867
868
		$args = wp_parse_args( $args, $defaults );
869
870
		if ( ! empty( $_GET['redirect_to'] ) ) {
871
			$args['redirect_to'] = esc_url_raw( $_GET['redirect_to'] );
872
		}
873
874
		return add_query_arg( $args, wp_login_url() );
875
	}
876
877
	/**
878
	 * Retrieves a WordPress.com SSO URL with appropriate query parameters or dies.
879
	 *
880
	 * @param  boolean  $reauth  Should the user be forced to reauthenticate on WordPress.com?
881
	 * @param  array    $args    Optional query parameters.
882
	 * @return string            The WordPress.com SSO URL.
883
	 */
884
	function get_sso_url_or_die( $reauth = false, $args = array() ) {
885
		if ( empty( $reauth ) ) {
886
			$sso_redirect = $this->build_sso_url( $args );
887
		} else {
888
			self::clear_wpcom_profile_cookies();
889
			$sso_redirect = $this->build_reauth_and_sso_url( $args );
890
		}
891
892
		// If there was an error retrieving the SSO URL, then error.
893
		if ( is_wp_error( $sso_redirect ) ) {
894
			$error_message = sanitize_text_field(
895
				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...
896
			);
897
			JetpackTracking::record_user_event( 'sso_login_redirect_failed', array(
898
				'error_message' => $error_message
899
			) );
900
			wp_die( $error_message );
901
		}
902
903
		return $sso_redirect;
904
	}
905
906
	/**
907
	 * Build WordPress.com SSO URL with appropriate query parameters.
908
	 *
909
	 * @param  array  $args Optional query parameters.
910
	 * @return string       WordPress.com SSO URL
911
	 */
912
	function build_sso_url( $args = array() ) {
913
		$sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
914
		$defaults = array(
915
			'action'    => 'jetpack-sso',
916
			'site_id'   => Jetpack_Options::get_option( 'id' ),
917
			'sso_nonce' => $sso_nonce,
918
		);
919
920
		$args = wp_parse_args( $args, $defaults );
921
922
		if ( is_wp_error( $args['sso_nonce'] ) ) {
923
			return $args['sso_nonce'];
924
		}
925
926
		return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
927
	}
928
929
	/**
930
	 * Build WordPress.com SSO URL with appropriate query parameters,
931
	 * including the parameters necessary to force the user to reauthenticate
932
	 * on WordPress.com.
933
	 *
934
	 * @param  array  $args Optional query parameters.
935
	 * @return string       WordPress.com SSO URL
936
	 */
937
	function build_reauth_and_sso_url( $args = array() ) {
938
		$sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
939
		$redirect = $this->build_sso_url( array( 'force_auth' => '1', 'sso_nonce' => $sso_nonce ) );
940
941
		if ( is_wp_error( $redirect ) ) {
942
			return $redirect;
943
		}
944
945
		$defaults = array(
946
			'action'      => 'jetpack-sso',
947
			'site_id'     => Jetpack_Options::get_option( 'id' ),
948
			'sso_nonce'   => $sso_nonce,
949
			'reauth'      => '1',
950
			'redirect_to' => urlencode( $redirect ),
951
		);
952
953
		$args = wp_parse_args( $args, $defaults );
954
955
		if ( is_wp_error( $args['sso_nonce'] ) ) {
956
			return $args['sso_nonce'];
957
		}
958
959
		return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
960
	}
961
962
	/**
963
	 * Determines local user associated with a given WordPress.com user ID.
964
	 *
965
	 * @since 2.6.0
966
	 *
967
	 * @param int $wpcom_user_id User ID from WordPress.com
968
	 * @return object Local user object if found, null if not.
969
	 */
970
	static function get_user_by_wpcom_id( $wpcom_user_id ) {
971
		$user_query = new WP_User_Query( array(
972
			'meta_key'   => 'wpcom_user_id',
973
			'meta_value' => intval( $wpcom_user_id ),
974
			'number'     => 1,
975
		) );
976
977
		$users = $user_query->get_results();
978
		return $users ? array_shift( $users ) : null;
979
	}
980
981
	/**
982
	 * Error message displayed on the login form when two step is required and
983
	 * the user's account on WordPress.com does not have two step enabled.
984
	 *
985
	 * @since 2.7
986
	 * @param string $message
987
	 * @return string
988
	 **/
989
	public function error_msg_enable_two_step( $message ) {
990
		$error = sprintf(
991
			wp_kses(
992
				__(
993
					'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.',
994
					'jetpack'
995
				),
996
				array(  'a' => array( 'href' => array() ) )
997
			),
998
			'https://wordpress.com/me/security/two-step',
999
			'https://support.wordpress.com/security/two-step-authentication/'
1000
		);
1001
1002
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
1003
1004
		return $message;
1005
	}
1006
1007
	/**
1008
	 * Error message displayed when the user tries to SSO, but match by email
1009
	 * is off and they already have an account with their email address on
1010
	 * this site.
1011
	 *
1012
	 * @param string $message
1013
	 * @return string
1014
	 */
1015
	public function error_msg_email_already_exists( $message ) {
1016
		$error = sprintf(
1017
			wp_kses(
1018
				__(
1019
					'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.',
1020
					'jetpack'
1021
				),
1022
				array(  'a' => array( 'href' => array() ) )
1023
			),
1024
			esc_url_raw( add_query_arg( 'jetpack-sso-default-form', '1', wp_login_url() ) )
1025
		);
1026
1027
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
1028
1029
		return $message;
1030
	}
1031
1032
	/**
1033
	 * Builds the translation ready string that is to be used when the site hides the default login form.
1034
	 *
1035
	 * @since 4.1.0
1036
	 * 
1037
	 * @return string
1038
	 */
1039
	public function get_sso_required_message() {
1040
		$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' );
1041
1042
		/**
1043
		 * Filter the message displayed when the default WordPress login form is disabled.
1044
		 *
1045
		 * @module sso
1046
		 *
1047
		 * @since 2.8.0
1048
		 *
1049
		 * @param string $msg Disclaimer when default WordPress login form is disabled.
1050
		 */
1051
		return apply_filters( 'jetpack_sso_disclaimer_message', $msg );
1052
	}
1053
1054
	/**
1055
	 * Message displayed when the site admin has disabled the default WordPress
1056
	 * login form in Settings > General > Single Sign On
1057
	 *
1058
	 * @since 2.7
1059
	 * @param string $message
1060
	 * 
1061
	 * @return string
1062
	 **/
1063
	public function msg_login_by_jetpack( $message ) {
1064
		$msg = $this->get_sso_required_message();
1065
1066
		if ( empty( $msg ) ) {
1067
			return $message;
1068
		}
1069
1070
		$message .= sprintf( '<p class="message">%s</p>', $msg );
1071
		return $message;
1072
	}
1073
1074
	/**
1075
	 * Message displayed when the user can not be found after approving the SSO process on WordPress.com
1076
	 *
1077
	 * @param string $message
1078
	 * @return string
1079
	 */
1080
	function cant_find_user( $message ) {
1081
		$error = esc_html__(
1082
			"We couldn't find your account. If you already have an account, make sure you have connected to WordPress.com.",
1083
			'jetpack'
1084
		);
1085
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $error );
1086
1087
		return $message;
1088
	}
1089
1090
	/**
1091
	 * When jetpack-sso-auth-redirect query parameter is set, will redirect user to
1092
	 * WordPress.com authorization flow.
1093
	 *
1094
	 * We redirect here instead of in handle_login() because Jetpack::init()->build_connect_url
1095
	 * calls menu_page_url() which doesn't work properly until admin menus are registered.
1096
	 */
1097
	function maybe_authorize_user_after_sso() {
1098
		if ( empty( $_GET['jetpack-sso-auth-redirect'] ) ) {
1099
			return;
1100
		}
1101
1102
		$redirect_to = ! empty( $_GET['redirect_to'] ) ? esc_url_raw( $_GET['redirect_to'] ) : admin_url();
1103
		$request_redirect_to = ! empty( $_GET['request_redirect_to'] ) ? esc_url_raw( $_GET['request_redirect_to'] ) : $redirect_to;
1104
1105
		/** This filter is documented in core/src/wp-login.php */
1106
		$redirect_after_auth = apply_filters( 'login_redirect', $redirect_to, $request_redirect_to, wp_get_current_user() );
1107
1108
		/**
1109
		 * Since we are passing this redirect to WordPress.com and therefore can not use wp_safe_redirect(),
1110
		 * let's sanitize it here to make sure it's safe. If the redirect is not safe, then use admin_url().
1111
		 */
1112
		$redirect_after_auth = wp_sanitize_redirect( $redirect_after_auth );
1113
		$redirect_after_auth = wp_validate_redirect( $redirect_after_auth, admin_url() );
1114
1115
		/**
1116
		 * Return the raw connect URL with our redirect and attribute connection to SSO.
1117
		 */
1118
		$connect_url = Jetpack::init()->build_connect_url( true, $redirect_after_auth, 'sso' );
1119
1120
		add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
1121
		wp_safe_redirect( $connect_url );
1122
		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...
1123
	}
1124
1125
	/**
1126
	 * Cache user's display name and Gravatar so it can be displayed on the login screen. These cookies are
1127
	 * stored when the user logs out, and then deleted when the user logs in.
1128
	 */
1129
	function store_wpcom_profile_cookies_on_logout() {
1130
		if ( ! Jetpack::is_user_connected( get_current_user_id() ) ) {
1131
			return;
1132
		}
1133
1134
		$user_data = $this->get_user_data( get_current_user_id() );
1135
		if ( ! $user_data ) {
1136
			return;
1137
		}
1138
1139
		setcookie(
1140
			'jetpack_sso_wpcom_name_' . COOKIEHASH,
1141
			$user_data->display_name,
1142
			time() + WEEK_IN_SECONDS,
1143
			COOKIEPATH,
1144
			COOKIE_DOMAIN
1145
		);
1146
1147
		setcookie(
1148
			'jetpack_sso_wpcom_gravatar_' . COOKIEHASH,
1149
			get_avatar_url(
1150
				$user_data->email,
1151
				array( 'size' => 72, 'default' => 'mystery' )
1152
			),
1153
			time() + WEEK_IN_SECONDS,
1154
			COOKIEPATH,
1155
			COOKIE_DOMAIN
1156
		);
1157
	}
1158
1159
	/**
1160
	 * Determines if a local user is connected to WordPress.com
1161
	 *
1162
	 * @since 2.8
1163
	 * @param integer $user_id - Local user id
1164
	 * @return boolean
1165
	 **/
1166
	public function is_user_connected( $user_id ) {
1167
		return $this->get_user_data( $user_id );
1168
	}
1169
1170
	/**
1171
	 * Retrieves a user's WordPress.com data
1172
	 *
1173
	 * @since 2.8
1174
	 * @param integer $user_id - Local user id
1175
	 * @return mixed null or stdClass
1176
	 **/
1177
	public function get_user_data( $user_id ) {
1178
		return get_user_meta( $user_id, 'wpcom_user_data', true );
1179
	}
1180
}
1181
1182
Jetpack_SSO::get_instance();
1183