Completed
Push — update/sso-bail-no-token ( 13d749...63dc37 )
by
unknown
89:02 queued 80:01
created

Jetpack_SSO::module_configuration_screen()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 8

Duplication

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