Completed
Push — add/sync-action ( ba3723...f7b4c8 )
by
unknown
09:22
created

Jetpack_SSO::get_sso_url_or_die()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
rs 9.4285
cc 3
eloc 9
nc 4
nop 2
1
<?php
2
3
/**
4
 * Module Name: Single Sign On
5
 * Module Description: Secure user authentication.
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: Jumpstart, Performance-Security
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, 'admin_init' ) );
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
33
		// Adding this action so that on login_init, the action won't be sanitized out of the $action global.
34
		add_action( 'login_form_jetpack-sso', '__return_true' );
35
36
		if (
37
			$this->should_hide_login_form() &&
38
			/**
39
			 * Filter the display of the disclaimer message appearing when default WordPress login form is disabled.
40
			 *
41
			 * @module sso
42
			 *
43
			 * @since 2.8.0
44
			 *
45
			 * @param bool true Should the disclaimer be displayed. Default to true.
46
			 */
47
			apply_filters( 'jetpack_sso_display_disclaimer', true )
48
		) {
49
			add_action( 'login_message', array( $this, 'msg_login_by_jetpack' ) );
50
		}
51
	}
52
53
	/**
54
	 * Returns the single instance of the Jetpack_SSO object
55
	 *
56
	 * @since 2.8
57
	 * @return Jetpack_SSO
58
	 **/
59
	public static function get_instance() {
60
		if ( ! is_null( self::$instance ) ) {
61
			return self::$instance;
62
		}
63
64
		return self::$instance = new Jetpack_SSO;
65
	}
66
67
	/**
68
	 * Add configure button and functionality to the module card on the Jetpack screen
69
	 **/
70
	public static function module_configure_button() {
71
		Jetpack::enable_module_configurable( __FILE__ );
72
		Jetpack::module_configuration_load( __FILE__, array( __CLASS__, 'module_configuration_load' ) );
73
		Jetpack::module_configuration_head( __FILE__, array( __CLASS__, 'module_configuration_head' ) );
74
		Jetpack::module_configuration_screen( __FILE__, array( __CLASS__, 'module_configuration_screen' ) );
75
	}
76
77
	public static function module_configuration_load() {
78
		// wp_safe_redirect( admin_url( 'options-general.php#configure-sso' ) );
79
		// exit;
80
	}
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
		 * Checkbox for Remove default login form
212
		 */
213
		 /* Hide in 2.9
214
		register_setting(
215
			'general',
216
			'jetpack_sso_remove_login_form',
217
			array( $this, 'validate_settings_remove_login_form_checkbox' )
218
		);
219
220
		add_settings_field(
221
			'jetpack_sso_remove_login_form',
222
			__( 'Remove default login form?' , 'jetpack' ),
223
			array( $this, 'render_remove_login_form_checkbox' ),
224
			'general',
225
			'jetpack_sso_settings'
226
		);
227
		*/
228
229
		/*
230
		 * Settings > General > Single Sign On
231
		 * Require two step authentication
232
		 */
233
		register_setting(
234
			'jetpack-sso',
235
			'jetpack_sso_require_two_step',
236
			array( $this, 'validate_jetpack_sso_require_two_step' )
237
		);
238
239
		add_settings_field(
240
			'jetpack_sso_require_two_step',
241
			'', // __( 'Require Two-Step Authentication' , 'jetpack' ),
242
			array( $this, 'render_require_two_step' ),
243
			'jetpack-sso',
244
			'jetpack_sso_settings'
245
		);
246
247
		/*
248
		 * Settings > General > Single Sign On
249
		 */
250
		register_setting(
251
			'jetpack-sso',
252
			'jetpack_sso_match_by_email',
253
			array( $this, 'validate_jetpack_sso_match_by_email' )
254
		);
255
256
		add_settings_field(
257
			'jetpack_sso_match_by_email',
258
			'', // __( 'Match by Email' , 'jetpack' ),
259
			array( $this, 'render_match_by_email' ),
260
			'jetpack-sso',
261
			'jetpack_sso_settings'
262
		);
263
	}
264
265
	/**
266
	 * Builds the display for the checkbox allowing user to require two step
267
	 * auth be enabled on WordPress.com accounts before login. Displays in Settings > General
268
	 *
269
	 * @since 2.7
270
	 **/
271
	public function render_require_two_step() {
272
		/** This filter is documented in modules/sso.php */
273
		$require_two_step = ( 1 == apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step' ) ) );
274
		$disabled = $require_two_step ? ' disabled="disabled"' : '';
275
		echo '<label>';
276
		echo '<input type="checkbox" name="jetpack_sso_require_two_step" ' . checked( $require_two_step, true, false ) . "$disabled>";
277
		esc_html_e( 'Require Two-Step Authentication' , 'jetpack' );
278
		echo '</label>';
279
	}
280
281
	/**
282
	 * Validate the require  two step checkbox in Settings > General
283
	 *
284
	 * @since 2.7
285
	 * @return boolean
286
	 **/
287
	public function validate_jetpack_sso_require_two_step( $input ) {
288
		return ( ! empty( $input ) ) ? 1 : 0;
289
	}
290
291
	/**
292
	 * Builds the display for the checkbox allowing the user to allow matching logins by email
293
	 * Displays in Settings > General
294
	 *
295
	 * @since 2.9
296
	 **/
297
	public function render_match_by_email() {
298
		$match_by_email = 1 == $this->match_by_email();
299
		$disabled = $match_by_email ? ' disabled="disabled"' : '';
300
		echo '<label>';
301
		echo '<input type="checkbox" name="jetpack_sso_match_by_email"' . checked( $match_by_email, true, false ) . "$disabled>";
302
		esc_html_e( 'Match by Email', 'jetpack' );
303
		echo '</label>';
304
	}
305
306
	/**
307
	 * Validate the match by email check in Settings > General
308
	 *
309
	 * @since 2.9
310
	 * @return boolean
311
	 **/
312
	public function validate_jetpack_sso_match_by_email( $input ) {
313
		return ( ! empty( $input ) ) ? 1 : 0;
314
	}
315
316
	/**
317
	 * Builds the display for the checkbox allowing users to remove the default
318
	 * WordPress login form from wp-login.php. Displays in Settings > General
319
	 *
320
	 * @since 2.7
321
	 **/
322
	public function render_remove_login_form_checkbox() {
323
		if ( $this->is_user_connected( get_current_user_id() ) ) {
324
			echo '<a name="configure-sso"></a>';
325
			echo '<input type="checkbox" name="jetpack_sso_remove_login_form[remove_login_form]" ' . checked( 1 == get_option( 'jetpack_sso_remove_login_form' ), true, false ) . '>';
326
			echo '<p class="description">Removes default login form and disallows login via POST</p>';
327
		} else {
328
			echo 'Your account must be connected to WordPress.com before disabling the login form.';
329
			echo '<br/>' . $this->button();
0 ignored issues
show
Bug introduced by
The method button() does not exist on Jetpack_SSO. Did you maybe mean module_configure_button()?

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...
330
		}
331
	}
332
333
	/**
334
	 * Validate settings input from Settings > General
335
	 *
336
	 * @since 2.7
337
	 * @return boolean
338
	 **/
339
	public function validate_settings_remove_login_form_checkbox( $input ) {
340
		return ( isset( $input['remove_login_form'] ) )? 1: 0;
341
	}
342
343
	/**
344
	 * Checks to determine if the user wants to login on wp-login
345
	 *
346
	 * This function mostly exists to cover the exceptions to login
347
	 * that may exist as other parameters to $_GET[action] as $_GET[action]
348
	 * does not have to exist. By default WordPress assumes login if an action
349
	 * is not set, however this may not be true, as in the case of logout
350
	 * where $_GET[loggedout] is instead set
351
	 *
352
	 * @return boolean
353
	 **/
354
	private function wants_to_login() {
355
		$wants_to_login = false;
356
357
		// Cover default WordPress behavior
358
		$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'login';
359
360
		// And now the exceptions
361
		$action = isset( $_GET['loggedout'] ) ? 'loggedout' : $action;
362
363
		if ( 'login' == $action ) {
364
			$wants_to_login = true;
365
		}
366
367
		return $wants_to_login;
368
	}
369
370
	private function bypass_login_forward_wpcom() {
371
		/**
372
		 * Redirect the site's log in form to WordPress.com's log in form.
373
		 *
374
		 * @module sso
375
		 *
376
		 * @since 3.1.0
377
		 *
378
		 * @param bool false Should the site's log in form be automatically forwarded to WordPress.com's log in form.
379
		 */
380
		return apply_filters( 'jetpack_sso_bypass_login_forward_wpcom', false );
381
	}
382
383
	function login_init() {
384
		global $action;
385
386
		/**
387
		 * If the user is attempting to logout AND the auto-forward to WordPress.com
388
		 * login is set then we need to ensure we do not auto-forward the user and get
389
		 * them stuck in an infinite logout loop.
390
		 */
391
		if ( isset( $_GET['loggedout'] ) && $this->bypass_login_forward_wpcom() ) {
392
			add_filter( 'jetpack_remove_login_form', '__return_true' );
393
		}
394
395
		/**
396
		 * Check to see if the site admin wants to automagically forward the user
397
		 * to the WordPress.com login page AND  that the request to wp-login.php
398
		 * is not something other than login (Like logout!)
399
		 */
400
		if (
401
			$this->wants_to_login()
402
			&& $this->bypass_login_forward_wpcom()
403
		) {
404
			add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
405
			$this->maybe_save_cookie_redirect();
406
			$reauth = ! empty( $_GET['reauth'] );
407
			wp_safe_redirect( $this->get_sso_url_or_die( $reauth ) );
408
			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...
409
		}
410
411
		if ( 'login' === $action ) {
412
			$this->display_sso_login_form();
413
		} elseif ( 'jetpack-sso' === $action ) {
414
			if ( isset( $_GET['result'], $_GET['user_id'], $_GET['sso_nonce'] ) && 'success' == $_GET['result'] ) {
415
				$this->handle_login();
416
				$this->display_sso_login_form();
417
			} else {
418
				if ( Jetpack::check_identity_crisis() ) {
419
					wp_die( __( "Error: This site's Jetpack connection is currently experiencing problems.", 'jetpack' ) );
420
				} else {
421
					$this->maybe_save_cookie_redirect();
422
					// Is it wiser to just use wp_redirect than do this runaround to wp_safe_redirect?
423
					add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
424
					$reauth = ! empty( $_GET['reauth'] );
425
					wp_safe_redirect( $this->get_sso_url_or_die( $reauth ) );
426
					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...
427
				}
428
			}
429
		}
430
	}
431
432
	/**
433
	 * Ensures that we can get a nonce from WordPress.com via XML-RPC before setting
434
	 * up the hooks required to display the SSO form.
435
	 */
436
	public function display_sso_login_form() {
437
		$sso_nonce = self::request_initial_nonce();
438
		if ( is_wp_error( $sso_nonce ) ) {
439
			return;
440
		}
441
442
		add_action( 'login_form',            array( $this, 'login_form' ) );
443
		add_filter( 'login_body_class',      array( $this, 'login_body_class' ) );
444
		add_action( 'login_enqueue_scripts', array( $this, 'login_enqueue_scripts' ) );
445
	}
446
447
	/**
448
	 * Conditionally save the redirect_to url as a cookie.
449
	 */
450
	public static function maybe_save_cookie_redirect() {
451
		if ( headers_sent() ) {
452
			return new WP_Error( 'headers_sent', __( 'Cannot deal with cookie redirects, as headers are already sent.', 'jetpack' ) );
453
		}
454
455
		if ( ! empty( $_GET['redirect_to'] ) ) {
456
			// If we have something to redirect to
457
			$url = esc_url_raw( $_GET['redirect_to'] );
458
			setcookie( 'jetpack_sso_redirect_to', $url, time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
459
460
		} elseif ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
461
			// Otherwise, if it's already set, purge it.
462
			setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
463
		}
464
465
		if ( ! empty( $_GET['rememberme'] ) ) {
466
			setcookie( 'jetpack_sso_remember_me', '1', time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, false, true );
467
		} elseif ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
468
			setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
469
		}
470
	}
471
472
	/**
473
	 * Determine if the login form should be hidden or not
474
	 *
475
	 * Method is private only because it is only used in this class so far.
476
	 * Feel free to change it later
477
	 *
478
	 * @return bool
479
	 **/
480
	private function should_hide_login_form() {
481
		/**
482
		 * Remove the default log in form, only leave the WordPress.com log in button.
483
		 *
484
		 * @module sso
485
		 *
486
		 * @since 3.1.0
487
		 *
488
		 * @param bool get_option( 'jetpack_sso_remove_login_form', false ) Should the default log in form be removed. Default to false.
489
		 */
490
		return apply_filters( 'jetpack_remove_login_form', get_option( 'jetpack_sso_remove_login_form', false ) );
491
	}
492
493
	/**
494
	 * Outputs the Jetpack SSO button and description as well as the toggle link
495
	 * for switching between Jetpack SSO and default login.
496
	 */
497
	function login_form() {
498
		$site_name = get_bloginfo( 'name' );
499
		if ( ! $site_name ) {
500
			$site_name = get_bloginfo( 'url' );
501
		}
502
503
		$display_name = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_name' . COOKIEHASH ] )
504
			? $_COOKIE[ 'jetpack_sso_wpcom_name' . COOKIEHASH ]
505
			: false;
506
		$gravatar = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_gravatar' . COOKIEHASH ] )
507
			? $_COOKIE[ 'jetpack_sso_wpcom_gravatar' . COOKIEHASH ]
508
			: false;
509
510
		?>
511
		<div id="jetpack-sso-wrap">
512
			<?php if ( $display_name && $gravatar ) : ?>
513
				<div id="jetpack-sso-wrap__user">
514
					<img width="72" height="72" src="<?php echo esc_html( $gravatar ); ?>" />
515
516
					<h2>
517
						<?php
518
							echo wp_kses(
519
								sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
520
								array( 'span' => true )
521
							);
522
						?>
523
					</h2>
524
				</div>
525
526
			<?php endif; ?>
527
528
529
			<div id="jetpack-sso-wrap__action">
530
				<?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...
531
532
				<?php if ( $display_name && $gravatar ) : ?>
533
					<a class="jetpack-sso-wrap__reauth" href="<?php echo $this->build_sso_button_url( array( 'reauth' => '1' ) ); ?>">
534
						<?php esc_html_e( 'Log in as a different WordPress.com user', 'jetpack' ); ?>
535
					</a>
536
				<?php else : ?>
537
					<p>
538
						<?php
539
							echo esc_html(
540
								sprintf(
541
									__( 'You can now save time spent logging in by connecting your WordPress.com account to %s.', 'jetpack' ),
542
									esc_html( $site_name )
543
								)
544
							);
545
						?>
546
					</p>
547
				<?php endif; ?>
548
			</div>
549
550
			<?php if ( ! $this->should_hide_login_form() ) : ?>
551
				<div class="jetpack-sso-or">
552
					<span><?php esc_html_e( 'Or', 'jetpack' ); ?></span>
553
				</div>
554
555
				<a href="<?php echo add_query_arg( 'jetpack-sso-default-form', '1' ); ?>" class="jetpack-sso-toggle wpcom">
556
					<?php
557
						esc_html_e( 'Log in with username and password', 'jetpack' )
558
					?>
559
				</a>
560
561
				<a href="<?php echo add_query_arg( 'jetpack-sso-default-form', '0' ); ?>" class="jetpack-sso-toggle default">
562
					<?php
563
						esc_html_e( 'Log in with WordPress.com', 'jetpack' )
564
					?>
565
				</a>
566
			<?php endif; ?>
567
		</div>
568
		<?php
569
	}
570
571
	/**
572
	 * Clear the cookies that store the profile information for the last
573
	 * WPCOM user to connect.
574
	 */
575
	static function clear_wpcom_profile_cookies() {
576 View Code Duplication
		if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_name' . COOKIEHASH ] ) ) {
577
			setcookie(
578
				'jetpack_sso_wpcom_name' . COOKIEHASH,
579
				' ',
580
				time() - YEAR_IN_SECONDS,
581
				COOKIEPATH,
582
				COOKIE_DOMAIN
583
			);
584
		}
585
586 View Code Duplication
		if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_gravatar' . COOKIEHASH ] ) ) {
587
			setcookie(
588
				'jetpack_sso_wpcom_gravatar' . COOKIEHASH,
589
				' ',
590
				time() - YEAR_IN_SECONDS,
591
				COOKIEPATH,
592
				COOKIE_DOMAIN
593
			);
594
		}
595
	}
596
597
	static function delete_connection_for_user( $user_id ) {
598
		if ( ! $wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true ) ) {
599
			return;
600
		}
601
		Jetpack::load_xml_rpc_client();
602
		$xml = new Jetpack_IXR_Client( array(
603
			'user_id' => $user_id,
604
		) );
605
		$xml->query( 'jetpack.sso.removeUser', $wpcom_user_id );
606
607
		if ( $xml->isError() ) {
608
			return false;
609
		}
610
611
		self::clear_wpcom_profile_cookies();
612
613
		return $xml->getResponse();
614
	}
615
616 View Code Duplication
	static function request_initial_nonce() {
617
		Jetpack::load_xml_rpc_client();
618
		$xml = new Jetpack_IXR_Client( array(
619
			'user_id' => get_current_user_id(),
620
		) );
621
		$xml->query( 'jetpack.sso.requestNonce' );
622
623
		if ( $xml->isError() ) {
624
			return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() );
625
		}
626
627
		return $xml->getResponse();
628
	}
629
630
	/**
631
	 * The function that actually handles the login!
632
	 */
633
	function handle_login() {
634
		$wpcom_nonce   = sanitize_key( $_GET['sso_nonce'] );
635
		$wpcom_user_id = (int) $_GET['user_id'];
636
		$result        = sanitize_key( $_GET['result'] );
0 ignored issues
show
Unused Code introduced by
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
637
638
		Jetpack::load_xml_rpc_client();
639
		$xml = new Jetpack_IXR_Client( array(
640
			'user_id' => get_current_user_id(),
641
		) );
642
		$xml->query( 'jetpack.sso.validateResult', $wpcom_nonce, $wpcom_user_id );
643
644
		if ( $xml->isError() ) {
645
			wp_die( sprintf( '%s: %s', $xml->getErrorCode(), $xml->getErrorMessage() ) );
646
		}
647
648
		$user_data = $xml->getResponse();
649
650
		if ( empty( $user_data ) ) {
651
			wp_die( __( 'Error, invalid response data.', 'jetpack' ) );
652
		}
653
654
		$user_data = (object) $user_data;
655
		$user = null;
656
657
		/**
658
		 * Fires before Jetpack's SSO modifies the log in form.
659
		 *
660
		 * @module sso
661
		 *
662
		 * @since 2.6.0
663
		 *
664
		 * @param object $user_data User login information.
665
		 */
666
		do_action( 'jetpack_sso_pre_handle_login', $user_data );
667
668
		/**
669
		 * Is it required to have 2-step authentication enabled on WordPress.com to use SSO?
670
		 *
671
		 * @module sso
672
		 *
673
		 * @since 2.8.0
674
		 *
675
		 * @param bool get_option( 'jetpack_sso_require_two_step' ) Does SSO require 2-step authentication?
676
		 */
677
		$require_two_step = apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step' ) );
678
		if ( $require_two_step && 0 == (int) $user_data->two_step_enabled ) {
679
			$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...
680
			/** This filter is documented in core/src/wp-includes/pluggable.php */
681
			do_action( 'wp_login_failed', $user_data->login );
682
			add_action( 'login_message', array( $this, 'error_msg_enable_two_step' ) );
683
			return;
684
		}
685
686
		if ( isset( $_GET['state'] ) && ( 0 < strpos( $_GET['state'], '|' ) ) ) {
687
			list( $state, $nonce ) = explode( '|', $_GET['state'] );
688
689
			if ( wp_verify_nonce( $nonce, $state ) ) {
690
				if ( 'sso-link-user' == $state ) {
691
					$user = wp_get_current_user();
692
					update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
693
					add_filter( 'login_redirect', array( __CLASS__, 'profile_page_url' ) );
694
				}
695
			} else {
696
				wp_nonce_ays();
697
			}
698
		}
699
700
		if ( empty( $user ) ) {
701
			$user = $this->get_user_by_wpcom_id( $user_data->ID );
702
		}
703
704
		// If we don't have one by wpcom_user_id, try by the email?
705
		if ( empty( $user ) && self::match_by_email() ) {
706
			$user = get_user_by( 'email', $user_data->email );
707
			if ( $user ) {
708
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
709
			}
710
		}
711
712
		// If we've still got nothing, create the user.
713
		if ( empty( $user ) && ( get_option( 'users_can_register' ) || self::new_user_override() ) ) {
714
			// If not matching by email we still need to verify the email does not exist
715
			// or this blows up
716
			/**
717
			 * If match_by_email is true, we know the email doesn't exist, as it would have
718
			 * been found in the first pass.  If get_user_by( 'email' ) doesn't find the
719
			 * user, then we know that email is unused, so it's safe to add.
720
			 */
721
			if ( self::match_by_email() || ! get_user_by( 'email', $user_data->email ) ) {
722
				$username = $user_data->login;
723
724
				if ( username_exists( $username ) ) {
725
					$username = $user_data->login . '_' . $user_data->ID;
726
				}
727
728
				$tries = 0;
729
				while ( username_exists( $username ) ) {
730
					$username = $user_data->login . '_' . $user_data->ID . '_' . mt_rand();
731
					if ( $tries++ >= 5 ) {
732
						wp_die( __( "Error: Couldn't create suitable username.", 'jetpack' ) );
733
					}
734
				}
735
736
				$password = wp_generate_password( 20 );
737
				$user_id  = wp_create_user( $username, $password, $user_data->email );
738
				$user     = get_userdata( $user_id );
739
740
				$user->display_name = $user_data->display_name;
741
				$user->first_name   = $user_data->first_name;
742
				$user->last_name    = $user_data->last_name;
743
				$user->url          = $user_data->url;
744
				$user->description  = $user_data->description;
745
				wp_update_user( $user );
746
747
				update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
748
			} else {
749
				$this->user_data = $user_data;
750
				// do_action( 'wp_login_failed', $user_data->login );
751
				add_action( 'login_message', array( $this, 'error_msg_email_already_exists' ) );
752
				return;
753
			}
754
		}
755
756
		/**
757
		 * Fires after we got login information from WordPress.com.
758
		 *
759
		 * @module sso
760
		 *
761
		 * @since 2.6.0
762
		 *
763
		 * @param array $user WordPress.com User information.
764
		 * @param object $user_data User Login information.
765
		 */
766
		do_action( 'jetpack_sso_handle_login', $user, $user_data );
767
768
		if ( $user ) {
769
			// Cache the user's details, so we can present it back to them on their user screen
770
			update_user_meta( $user->ID, 'wpcom_user_data', $user_data );
771
772
			// Cache user's display name and Gravatar so it can be displayed on the login screen
773
			setcookie(
774
				'jetpack_sso_wpcom_name' . COOKIEHASH,
775
				$user_data->display_name,
776
				time() + YEAR_IN_SECONDS,
777
				COOKIEPATH,
778
				COOKIE_DOMAIN
779
			);
780
781
			setcookie(
782
				'jetpack_sso_wpcom_gravatar' . COOKIEHASH,
783
				get_avatar_url(
784
					$user_data->email,
785
					array( 'size' => 72, 'default' => 'mystery' )
786
				),
787
				time() + YEAR_IN_SECONDS,
788
				COOKIEPATH,
789
				COOKIE_DOMAIN
790
			);
791
792
			$remember = false;
793 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_remember_me'] ) ) {
794
				$remember = true;
795
				// And then purge it
796
				setcookie( 'jetpack_sso_remember_me', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
797
			}
798
			/**
799
			 * Filter the remember me value.
800
			 *
801
			 * @module sso
802
			 *
803
			 * @since 2.8.0
804
			 *
805
			 * @param bool $remember Is the remember me option checked?
806
			 */
807
			$remember = apply_filters( 'jetpack_remember_login', $remember );
808
			wp_set_auth_cookie( $user->ID, $remember );
809
810
			/** This filter is documented in core/src/wp-includes/user.php */
811
			do_action( 'wp_login', $user->user_login, $user );
812
813
			$_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '';
814
			$redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : self::profile_page_url();
815
816
			// If we have a saved redirect to request in a cookie
817 View Code Duplication
			if ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
818
				// Set that as the requested redirect to
819
				$redirect_to = $_request_redirect_to = esc_url_raw( $_COOKIE['jetpack_sso_redirect_to'] );
820
				// And then purge it
821
				setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
822
			}
823
824
			wp_safe_redirect(
825
				/** This filter is documented in core/src/wp-login.php */
826
				apply_filters( 'login_redirect', $redirect_to, $_request_redirect_to, $user )
827
			);
828
			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...
829
		}
830
831
		$this->user_data = $user_data;
832
		/** This filter is documented in core/src/wp-includes/pluggable.php */
833
		do_action( 'wp_login_failed', $user_data->login );
834
		add_action( 'login_message', array( $this, 'cant_find_user' ) );
835
	}
836
837
	static function profile_page_url() {
838
		return admin_url( 'profile.php' );
839
	}
840
841
	static function match_by_email() {
842
		$match_by_email = ( 1 == get_option( 'jetpack_sso_match_by_email', true ) ) ? true: false;
843
		$match_by_email = defined( 'WPCC_MATCH_BY_EMAIL' ) ? WPCC_MATCH_BY_EMAIL : $match_by_email;
844
845
		/**
846
		 * Link the local account to an account on WordPress.com using the same email address.
847
		 *
848
		 * @module sso
849
		 *
850
		 * @since 2.6.0
851
		 *
852
		 * @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.
853
		 */
854
		return apply_filters( 'jetpack_sso_match_by_email', $match_by_email );
855
	}
856
857
	static function new_user_override() {
858
		$new_user_override = defined( 'WPCC_NEW_USER_OVERRIDE' ) ? WPCC_NEW_USER_OVERRIDE : false;
859
860
		/**
861
		 * Allow users to register on your site with a WordPress.com account, even though you disallow normal registrations.
862
		 *
863
		 * @module sso
864
		 *
865
		 * @since 2.6.0
866
		 *
867
		 * @param bool $new_user_override Allow users to register on your site with a WordPress.com account. Default to false.
868
		 */
869
		return apply_filters( 'jetpack_sso_new_user_override', $new_user_override );
870
	}
871
872
	function allowed_redirect_hosts( $hosts ) {
873
		if ( empty( $hosts ) ) {
874
			$hosts = array();
875
		}
876
		$hosts[] = 'wordpress.com';
877
878
		return array_unique( $hosts );
879
	}
880
881
	/**
882
	 * Builds the "Login to WordPress.com" button that is displayed on the login page as well as user profile page.
883
	 *
884
	 * @param  array   $args       An array of arguments to add to the SSO URL.
885
	 * @param  boolean $is_primary Should the button have the `button-primary` class?
886
	 * @return string              Returns the HTML markup for the button.
887
	 */
888
	function build_sso_button( $args = array(), $is_primary = false ) {
889
		$url = $this->build_sso_button_url( $args );
890
		$classes = $is_primary
891
			? 'jetpack-sso button button-primary'
892
			: 'jetpack-sso button';
893
894
		return sprintf(
895
			'<a href="%1$s" class="%2$s">%3$s</a>',
896
			esc_url( $url ),
897
			$classes,
898
			esc_html__( 'Log in with WordPress.com', 'jetpack' )
899
		);
900
	}
901
902
	/**
903
	 * Builds a URL with `jetpack-sso` action and option args which is used to setup SSO.
904
	 *
905
	 * @param  array  $args An array of arguments to add to the SSO URL.
906
	 * @return string       The URL used for SSO.
907
	 */
908
	function build_sso_button_url( $args = array() ) {
909
		$defaults = array(
910
			'action'  => 'jetpack-sso',
911
		);
912
913
		$args = wp_parse_args( $args, $defaults );
914
915
		if ( ! empty( $_GET['redirect_to'] ) ) {
916
			$args['redirect_to'] = esc_url_raw( $_GET['redirect_to'] );
917
		}
918
919
		return add_query_arg( $args, wp_login_url() );
920
	}
921
922
	/**
923
	 * Retrieves a WordPress.com SSO URL with appropriate query parameters or dies.
924
	 *
925
	 * @param  boolean  $reauth  Should the user be forced to reauthenticate on WordPress.com?
926
	 * @param  array    $args    Optional query parameters.
927
	 * @return string            The WordPress.com SSO URL.
928
	 */
929
	function get_sso_url_or_die( $reauth = false, $args = array() ) {
930
		if ( empty( $reauth ) ) {
931
			$sso_redirect = $this->build_sso_url( $args );
932
		} else {
933
			self::clear_wpcom_profile_cookies();
934
			$sso_redirect = $this->build_reauth_and_sso_url( $args );
935
		}
936
937
		// If there was an error retrieving the SSO URL, then error.
938
		if ( is_wp_error( $sso_redirect ) ) {
939
			wp_die( 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...
940
		}
941
942
		return $sso_redirect;
943
	}
944
945
	/**
946
	 * Build WordPress.com SSO URL with appropriate query parameters.
947
	 *
948
	 * @param  array  $args Optional query parameters.
949
	 * @return string       WordPress.com SSO URL
950
	 */
951
	function build_sso_url( $args = array() ) {
952
		$sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
953
		$defaults = array(
954
			'action'    => 'jetpack-sso',
955
			'site_id'   => Jetpack_Options::get_option( 'id' ),
956
			'sso_nonce' => $sso_nonce,
957
		);
958
959
		if ( isset( $_GET['state'] ) && check_admin_referer( $_GET['state'] ) ) {
960
			$defaults['state'] = rawurlencode( $_GET['state'] . '|' . $_GET['_wpnonce'] );
961
		}
962
963
		$args = wp_parse_args( $args, $defaults );
964
965
		if ( is_wp_error( $args['sso_nonce'] ) ) {
966
			return $args['sso_nonce'];
967
		}
968
969
		return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
970
	}
971
972
	/**
973
	 * Build WordPress.com SSO URL with appropriate query parameters,
974
	 * including the parameters necessary to force the user to reauthenticate
975
	 * on WordPress.com.
976
	 *
977
	 * @param  array  $args Optional query parameters.
978
	 * @return string       WordPress.com SSO URL
979
	 */
980
	function build_reauth_and_sso_url( $args = array() ) {
981
		$sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
982
983
		if ( is_wp_error( $redirect ) ) {
0 ignored issues
show
Bug introduced by
The variable $redirect seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
984
			return $redirect;
0 ignored issues
show
Bug introduced by
The variable $redirect seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
985
		}
986
987
		$redirect = $this->build_sso_url( array( 'force_auth' => '1', 'sso_nonce' => $sso_nonce ) );
988
		$defaults = array(
989
			'action'      => 'jetpack-sso',
990
			'site_id'     => Jetpack_Options::get_option( 'id' ),
991
			'sso_nonce'   => $sso_nonce,
992
			'reauth'      => '1',
993
			'redirect_to' => urlencode( $redirect ),
994
		);
995
996
		$args = wp_parse_args( $args, $defaults );
997
998
		if ( is_wp_error( $args['sso_nonce'] ) ) {
999
			return $args['sso_nonce'];
1000
		}
1001
1002
		return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
1003
	}
1004
1005
	/**
1006
	 * Determines local user associated with a given WordPress.com user ID.
1007
	 *
1008
	 * @since 2.6.0
1009
	 *
1010
	 * @param int $wpcom_user_id User ID from WordPress.com
1011
	 * @return object Local user object if found, null if not.
1012
	 */
1013
	static function get_user_by_wpcom_id( $wpcom_user_id ) {
1014
		$user_query = new WP_User_Query( array(
1015
			'meta_key'   => 'wpcom_user_id',
1016
			'meta_value' => intval( $wpcom_user_id ),
1017
			'number'     => 1,
1018
		) );
1019
1020
		$users = $user_query->get_results();
1021
		return $users ? array_shift( $users ) : null;
1022
	}
1023
1024
	/**
1025
	 * Error message displayed on the login form when two step is required and
1026
	 * the user's account on WordPress.com does not have two step enabled.
1027
	 *
1028
	 * @since 2.7
1029
	 * @param string $message
1030
	 * @return string
1031
	 **/
1032
	public function error_msg_enable_two_step( $message ) {
1033
		$err = __( sprintf( 'This site requires two step authentication be enabled for your user account on WordPress.com. Please visit the <a href="%1$s" target="_blank"> Security Settings</a> page to enable two step', 'https://wordpress.com/me/security/two-step' ) , 'jetpack' );
1034
1035
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
1036
1037
		return $message;
1038
	}
1039
1040
	/**
1041
	 * Error message displayed when the user tries to SSO, but match by email
1042
	 * is off and they already have an account with their email address on
1043
	 * this site.
1044
	 *
1045
	 * @param string $message
1046
	 * @return string
1047
	 */
1048
	public function error_msg_email_already_exists( $message ) {
1049
		$err = __( sprintf( 'You already have an account on this site. Please visit your <a href="%1$s">profile page</a> page to link your account to WordPress.com!', admin_url( 'profile.php' ) ) , 'jetpack' );
1050
1051
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
1052
1053
		return $message;
1054
	}
1055
1056
	/**
1057
	 * Message displayed when the site admin has disabled the default WordPress
1058
	 * login form in Settings > General > Single Sign On
1059
	 *
1060
	 * @since 2.7
1061
	 * @param string $message
1062
	 * @return string
1063
	 **/
1064
	public function msg_login_by_jetpack( $message ) {
1065
1066
		$msg = __( sprintf( 'Jetpack authenticates through WordPress.com — to log in, enter your WordPress.com username and password, or <a href="%1$s" target="_blank">visit WordPress.com</a> to create a free account now.', 'http://wordpress.com/signup' ) , 'jetpack' );
1067
1068
		/**
1069
		 * Filter the message displayed when the default WordPress login form is disabled.
1070
		 *
1071
		 * @module sso
1072
		 *
1073
		 * @since 2.8.0
1074
		 *
1075
		 * @param string $msg Disclaimer when default WordPress login form is disabled.
1076
		 */
1077
		$msg = apply_filters( 'jetpack_sso_disclaimer_message', $msg );
1078
1079
		$message .= sprintf( '<p class="message">%s</p>', $msg );
1080
		return $message;
1081
	}
1082
1083
	/**
1084
	 * Error message displayed on the login form when the user attempts
1085
	 * to post to the login form and it is disabled.
1086
	 *
1087
	 * @since 2.8
1088
	 * @param string $message
1089
	 * @param string
1090
	 **/
1091
	public function error_msg_login_method_not_allowed( $message ) {
1092
		$err = __( 'Login method not allowed' , 'jetpack' );
1093
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
1094
1095
		return $message;
1096
	}
1097
	function cant_find_user( $message ) {
1098
		if ( self::match_by_email() ) {
1099
			$err_format = __( 'We couldn\'t find an account with the email <strong><code>%1$s</code></strong> to log you in with.  If you already have an account on <strong>%2$s</strong>, please make sure that <strong><code>%1$s</code></strong> is configured as the email address, or that you have connected to WordPress.com on your profile page.', 'jetpack' );
1100
		} else {
1101
			$err_format = __( 'We couldn\'t find any account on <strong>%2$s</strong> that is linked to your WordPress.com account to log you in with.  If you already have an account on <strong>%2$s</strong>, please make sure that you have connected to WordPress.com on your profile page.', 'jetpack' );
1102
		}
1103
		$err = sprintf( $err_format, $this->user_data->email, get_bloginfo( 'name' ) );
1104
		$message .= sprintf( '<p class="message" id="login_error">%s</p>', $err );
1105
		return $message;
1106
	}
1107
1108
	/**
1109
	 * Deal with user connections...
1110
	 */
1111
	function admin_init() {
1112
		add_action( 'show_user_profile', array( $this, 'edit_profile_fields' ) ); // For their own profile
1113
		add_action( 'edit_user_profile', array( $this, 'edit_profile_fields' ) ); // For folks editing others profiles
1114
1115
		if ( isset( $_GET['jetpack_sso'] ) && 'purge' == $_GET['jetpack_sso'] && check_admin_referer( 'jetpack_sso_purge' ) ) {
1116
			$user = wp_get_current_user();
1117
			// Remove the connection on the wpcom end.
1118
			self::delete_connection_for_user( $user->ID );
1119
			// Clear it locally.
1120
			delete_user_meta( $user->ID, 'wpcom_user_id' );
1121
			delete_user_meta( $user->ID, 'wpcom_user_data' );
1122
			// Forward back to the profile page.
1123
			wp_safe_redirect( remove_query_arg( array( 'jetpack_sso', '_wpnonce' ) ) );
1124
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_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...
1125
		}
1126
	}
1127
1128
	/**
1129
	 * Determines if a local user is connected to WordPress.com
1130
	 *
1131
	 * @since 2.8
1132
	 * @param integer $user_id - Local user id
1133
	 * @return boolean
1134
	 **/
1135
	public function is_user_connected( $user_id ) {
1136
		return $this->get_user_data( $user_id );
1137
	}
1138
1139
	/**
1140
	 * Retrieves a user's WordPress.com data
1141
	 *
1142
	 * @since 2.8
1143
	 * @param integer $user_id - Local user id
1144
	 * @return mixed null or stdClass
1145
	 **/
1146
	public function get_user_data( $user_id ) {
1147
		return get_user_meta( $user_id, 'wpcom_user_data', true );
1148
	}
1149
1150
	function edit_profile_fields( $user ) {
1151
		?>
1152
1153
		<h3 id="single-sign-on"><?php _e( 'Single Sign On', 'jetpack' ); ?></h3>
1154
		<p><?php _e( 'Connecting with Single Sign On enables you to log in via your WordPress.com account.', 'jetpack' ); ?></p>
1155
1156
		<?php if ( $this->is_user_connected( $user->ID ) ) : /* If the user is currently connected... */ ?>
1157
			<?php $user_data = $this->get_user_data( $user->ID ); ?>
1158
			<table class="form-table jetpack-sso-form-table">
1159
				<tbody>
1160
					<tr>
1161
						<td>
1162
							<div class="profile-card">
1163
								<?php echo get_avatar( $user_data->email ); ?>
1164
								<p class="connected"><strong><?php _e( 'Connected', 'jetpack' ); ?></strong></p>
1165
								<p><?php echo esc_html( $user_data->login ); ?></p>
1166
								<span class="two_step">
1167
									<?php
1168
										if ( $user_data->two_step_enabled ) {
1169
											?> <p class="enabled"><a href="https://wordpress.com/me/security/two-step" target="_blank"><?php _e( 'Two-Step Authentication Enabled', 'jetpack' ); ?></a></p> <?php
1170
										} else {
1171
											?> <p class="disabled"><a href="https://wordpress.com/me/security/two-step" target="_blank"><?php _e( 'Two-Step Authentication Disabled', 'jetpack' ); ?></a></p> <?php
1172
										}
1173
									?>
1174
								</span>
1175
1176
							</div>
1177
							<p><a class="button button-secondary" href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'jetpack_sso', 'purge' ), 'jetpack_sso_purge' ) ); ?>"><?php _e( 'Unlink This Account', 'jetpack' ); ?></a></p>
1178
						</td>
1179
					</tr>
1180
				</tbody>
1181
			</table>
1182
		<?php elseif ( get_current_user_id() == $user->ID && Jetpack::is_user_connected( $user->ID ) ) : ?>
1183
1184
			<?php echo $this->build_sso_button( 'state=sso-link-user&_wpnonce=' . wp_create_nonce( 'sso-link-user' ) ); ?>
0 ignored issues
show
Documentation introduced by
'state=sso-link-user&_wp..._nonce('sso-link-user') is of type string, but the function expects a array.

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...
1185
1186
		<?php else : ?>
1187
1188
			<p><?php esc_html_e( wptexturize( __( "If you don't have a WordPress.com account yet, you can sign up for free in just a few seconds.", 'jetpack' ) ) ); ?></p>
1189
			<a href="<?php echo Jetpack::init()->build_connect_url( false, get_edit_profile_url( get_current_user_id() ) . '#single-sign-on' ); ?>" class="button button-connector" id="wpcom-connect"><?php esc_html_e( 'Link account with WordPress.com', 'jetpack' ); ?></a>
1190
1191
		<?php endif;
1192
	}
1193
}
1194
1195
Jetpack_SSO::get_instance();
1196