Completed
Push — 4.1.0/videopress-media-merge ( 41c2e2...e72d1f )
by George
09:19
created

Jetpack_SSO   D

Complexity

Total Complexity 137

Size/Duplication

Total Lines 1131
Duplicated Lines 5.31 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 6
Bugs 1 Features 0
Metric Value
wmc 137
c 6
b 1
f 0
lcom 2
cbo 3
dl 60
loc 1131
rs 4

47 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 34 3
A get_instance() 0 7 2
A module_configure_button() 0 6 1
A module_configuration_load() 0 4 1
A module_configuration_head() 0 1 1
A module_configuration_screen() 0 9 1
A build_sso_button_url() 0 13 2
A build_sso_url() 0 14 3
A build_reauth_and_sso_url() 0 12 1
A get_user_by_wpcom_id() 0 10 2
A error_msg_enable_two_step() 0 7 1
A error_msg_email_already_exists() 0 7 1
A msg_login_by_jetpack() 0 18 1
A error_msg_login_method_not_allowed() 0 6 1
A cant_find_user() 0 10 2
A maybe_logout_user() 0 11 2
A xmlrpc_methods() 0 4 1
A xmlrpc_user_disconnect() 0 18 2
A login_enqueue_scripts() 0 10 2
A admin_enqueue_scripts() 0 9 3
A register_settings() 0 64 1
A render_require_two_step() 0 9 2
A validate_jetpack_sso_require_two_step() 0 3 2
A render_match_by_email() 0 8 2
A validate_jetpack_sso_match_by_email() 0 3 2
A render_remove_login_form_checkbox() 0 10 2
A wants_to_login() 0 15 4
A bypass_login_forward_wpcom() 0 12 1
C login_init() 0 56 11
B maybe_save_cookie_redirect() 0 21 6
A clear_wpcom_profile_cookies() 18 21 3
A request_initial_nonce() 13 13 2
F handle_login() 11 203 26
A profile_page_url() 0 3 1
A match_by_email() 0 15 3
A new_user_override() 0 14 2
A allowed_redirect_hosts() 0 8 2
A login_body_class() 0 15 4
A validate_settings_remove_login_form_checkbox() 0 3 2
A should_hide_login_form() 0 12 1
C login_form() 0 73 9
A build_sso_button() 0 13 2
A admin_init() 0 16 4
A is_user_connected() 0 3 1
A get_user_data() 0 3 1
B edit_profile_fields() 0 43 5
A delete_connection_for_user() 18 18 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_SSO often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_SSO, and based on these observations, apply Extract Interface, too.

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